[{"data":1,"prerenderedAt":1308},["ShallowReactive",2],{"content:\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties":3,"surroundings:\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties":1299},{"id":4,"title":5,"body":6,"description":1280,"extension":1281,"meta":1282,"navigation":536,"path":1293,"seo":1294,"stem":1297,"__hash__":1298},"content\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties\u002Findex.md","Fixing CLS from Animating Layout Properties",{"type":7,"value":8,"toc":1266},"minimark",[9,14,42,54,198,203,242,246,277,286,292,298,434,438,443,638,641,645,777,780,784,787,898,901,905,908,994,1001,1082,1086,1095,1099,1102,1112,1134,1156,1165,1169,1195,1208,1224,1228,1251,1256,1259,1262],[10,11,13],"h1",{"id":12},"how-to-fix-cls-from-animations-on-top-left-height-and-margin","How to Fix CLS from Animations on top, left, height and margin",[15,16,17,18,23,24,28,29,33,34,37,38,41],"p",{},"This guide covers a CLS source that often surprises teams, within ",[19,20,22],"a",{"href":21},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002F","Reducing Cumulative Layout Shift (CLS)"," and ",[19,25,27],{"href":26},"\u002Fcore-web-vitals-measurement\u002F","Core Web Vitals & Measurement",": animations. A carousel that slides by animating ",[30,31,32],"code",{},"left",", an accordion that opens by animating ",[30,35,36],{},"height",", a notification that slides in by animating ",[30,39,40],{},"top"," — each changes layout on every frame, and every frame can produce a layout shift entry.",[15,43,44,45,48,49,53],{},"Not every animation counts. Shifts within 500ms of a discrete user input (click, tap, keypress) are excluded, and changes made with CSS ",[30,46,47],{},"transform"," do not generate layout shifts at all because they do not change layout. The CLS problem arises from animations that run ",[50,51,52],"em",{},"without"," recent input — autoplay carousels, timed banners, scroll-triggered reveals — or that outlast the 500ms exclusion window, and that use layout properties instead of transforms.",[15,55,56],{},[57,58,64,65,64,72,64,76,64,79,64,88,64,94,64,105,64,110,64,118,64,125,64,131,64,135,64,139,64,142,64,146,64,151,64,157,64,161,64,165,64,169,64,173,64,176,64,180,64,184,64,188,64,192,64],"svg",{"viewBox":59,"width":60,"role":61,"ariaLabel":62,"style":63},"0 0 760 200","100%","img","Timeline of a height animation that starts on click and continues past the 500 millisecond input exclusion, so its later frames count as layout shifts.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[66,67],"rect",{"className":68,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],"svg-canvas","0","#ffffff",[73,74,75],"title",{},"An accordion animated with height",[77,78,62],"desc",{},[66,80],{"x":81,"y":81,"width":82,"height":83,"rx":84,"fill":85,"stroke":86,"style":87},"1","758","198","10","none","currentColor","stroke-opacity:0.18",[89,90,75],"text",{"x":91,"y":92,"fill":86,"style":93},"28.0","34.0","font-size:16px;font-weight:700",[95,96],"line",{"x1":97,"y1":98,"x2":97,"y2":99,"stroke":100,"strokeWidth":101,"strokeDashArray":102,"style":104},"453.9","72.0","156.0","#b8860b","1.5",[103,103],"4","stroke-opacity:0.9",[89,106,109],{"x":91,"y":107,"fill":86,"style":108},"96.0","font-size:12.5px;font-weight:700","Animation",[66,111],{"x":112,"y":113,"width":114,"height":115,"rx":116,"fill":86,"stroke":86,"style":117},"114.6","78.0","338.6","26.0","3","fill-opacity:0.06;stroke-opacity:0.4",[89,119,124],{"x":120,"y":121,"fill":86,"style":122,"textAnchor":123},"283.9","95.5","font-size:11.5px","middle","frames excluded",[66,126],{"x":127,"y":113,"width":128,"height":115,"rx":116,"fill":129,"stroke":100,"style":130},"454.7","202.6","#ffc300","fill-opacity:0.24;stroke-opacity:0.9",[89,132,134],{"x":133,"y":121,"fill":86,"style":122,"textAnchor":123},"556.0","frames counted",[89,136,138],{"x":91,"y":137,"fill":86,"style":108},"134.0","Shift entries",[66,140],{"x":127,"y":141,"width":128,"height":115,"rx":116,"fill":129,"stroke":100,"style":130},"116.0",[89,143,145],{"x":133,"y":144,"fill":86,"style":122,"textAnchor":123},"133.5","~18 small shifts",[95,147],{"x1":148,"y1":99,"x2":149,"y2":99,"stroke":86,"style":150},"113.8","726.0","stroke-opacity:0.4",[89,152,156],{"x":153,"y":154,"fill":155,"style":122,"textAnchor":123},"124.5","173.0","#51617a","0ms",[89,158,160],{"x":159,"y":154,"fill":155,"style":122,"textAnchor":123},"181.8","100ms",[89,162,164],{"x":163,"y":154,"fill":155,"style":122,"textAnchor":123},"249.9","200ms",[89,166,168],{"x":167,"y":154,"fill":155,"style":122,"textAnchor":123},"317.9","300ms",[89,170,172],{"x":171,"y":154,"fill":155,"style":122,"textAnchor":123},"385.9","400ms",[89,174,175],{"x":97,"y":154,"fill":155,"style":122,"textAnchor":123},"500ms",[89,177,179],{"x":178,"y":154,"fill":155,"style":122,"textAnchor":123},"521.9","600ms",[89,181,183],{"x":182,"y":154,"fill":155,"style":122,"textAnchor":123},"590.0","700ms",[89,185,187],{"x":186,"y":154,"fill":155,"style":122,"textAnchor":123},"658.0","800ms",[89,189,191],{"x":190,"y":154,"fill":155,"style":122,"textAnchor":123},"715.3","900ms",[89,193,197],{"x":97,"y":194,"fill":195,"style":196,"textAnchor":123},"68.0","#8a6608","font-size:12px;font-weight:700","500ms exclusion ends",[199,200,202],"h2",{"id":201},"rapid-diagnosis","Rapid Diagnosis",[204,205,206,214,224,230,236],"ul",{},[207,208,209,213],"li",{},[210,211,212],"strong",{},"Look for clusters of tiny shifts."," In the Performance panel's Layout Shifts track, an animation shows as a dense series of small shifts at frame intervals rather than one large one.",[207,215,216,223],{},[210,217,218,219,222],{},"Check ",[30,220,221],{},"hadRecentInput","."," Log shift entries; those during the first 500ms after a click are flagged and excluded, later ones are not.",[207,225,226,229],{},[210,227,228],{},"Inspect the animated properties."," In the Animations drawer (More tools → Animations), capture the animation and read which properties it animates.",[207,231,232,235],{},[210,233,234],{},"Use the Rendering drawer's \"Layout Shift Regions\"."," Animated regions flashing blue repeatedly confirm layout-driven animation.",[207,237,238,241],{},[210,239,240],{},"Check autoplay."," Carousels, tickers and rotating banners animate without input; every shift they cause counts.",[199,243,245],{"id":244},"root-cause-analysis","Root Cause Analysis",[15,247,248,64,251,253,254,253,256,253,259,253,262,253,265,253,267,253,270,23,273,276],{},[210,249,250],{},"1. Layout properties change geometry.",[30,252,40],{},", ",[30,255,32],{},[30,257,258],{},"right",[30,260,261],{},"bottom",[30,263,264],{},"width",[30,266,36],{},[30,268,269],{},"margin",[30,271,272],{},"padding",[30,274,275],{},"font-size"," all change the element's layout box. Changing the box of an element in normal flow moves its siblings — that movement is a layout shift.",[15,278,279,282,283,285],{},[210,280,281],{},"2. Positioned elements still count."," Animating ",[30,284,40],{}," on an absolutely positioned element does not move siblings, but it moves the element itself, and the element's own movement is a shift if it is visible and was not just added.",[15,287,288,291],{},[210,289,290],{},"3. Long or delayed animations escape the exclusion."," A 300ms transition triggered by a click is excluded. The same transition with a 400ms delay, or an 800ms duration, has frames outside the window.",[15,293,294,297],{},[210,295,296],{},"4. Animation without input."," Autoplay and scroll-linked animations never have recent input. Scrolling is not a discrete input for this purpose, so scroll-driven reveals that animate layout properties count in full.",[15,299,300],{},[57,301,64,304,64,307,64,310,64,312,64,315,64,317,64,322,64,328,64,332,64,336,64,339,64,342,64,345,64,349,64,353,64,357,64,359,64,363,64,365,64,368,64,370,64,373,64,375,64,379,64,381,64,384,64,386,64,388,64,390,64,392,64,395,64,398,64,402,64,405,64,407,64,410,64,412,64,415,64,418,64,422,64,424,64,426,64,428,64,430,64,432,64],{"viewBox":302,"width":60,"role":61,"ariaLabel":303,"style":63},"0 0 760 228","Classification of commonly animated CSS properties by whether they change layout, generate shift entries, and their rendering cost.",[66,305],{"className":306,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],[73,308,309],{},"Which property changes produce layout shifts",[77,311,303],{},[66,313],{"x":81,"y":81,"width":82,"height":314,"rx":84,"fill":85,"stroke":86,"style":87},"226",[89,316,309],{"x":91,"y":92,"fill":86,"style":93},[66,318],{"x":91,"y":319,"width":320,"height":321,"rx":70,"fill":86,"stroke":86,"style":117},"56.0","147.9","30.0",[89,323,327],{"x":324,"y":325,"fill":86,"style":108,"textAnchor":326},"38.0","75.5","start","Property",[66,329],{"x":330,"y":319,"width":331,"height":321,"rx":70,"fill":86,"stroke":86,"style":117},"175.9","185.4",[89,333,335],{"x":334,"y":325,"fill":86,"style":108,"textAnchor":123},"268.6","Changes layout?",[66,337],{"x":338,"y":319,"width":331,"height":321,"rx":70,"fill":86,"stroke":86,"style":117},"361.2",[89,340,341],{"x":97,"y":325,"fill":86,"style":108,"textAnchor":123},"Counts toward CLS?",[66,343],{"x":344,"y":319,"width":331,"height":321,"rx":70,"fill":86,"stroke":86,"style":117},"546.6",[89,346,348],{"x":347,"y":325,"fill":86,"style":108,"textAnchor":123},"639.3","Cost per frame",[66,350],{"x":91,"y":351,"width":320,"height":321,"rx":70,"fill":85,"stroke":86,"style":352},"86.0","stroke-opacity:0.35",[89,354,356],{"x":324,"y":355,"fill":86,"style":108,"textAnchor":326},"105.5","top \u002F left",[66,358],{"x":330,"y":351,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,360,362],{"x":334,"y":355,"fill":86,"style":361,"textAnchor":123},"font-size:12px","yes, for the element",[66,364],{"x":338,"y":351,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,366,367],{"x":97,"y":355,"fill":86,"style":361,"textAnchor":123},"yes, without input",[66,369],{"x":344,"y":351,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,371,372],{"x":347,"y":355,"fill":86,"style":361,"textAnchor":123},"layout + paint",[66,374],{"x":91,"y":141,"width":320,"height":321,"rx":70,"fill":85,"stroke":86,"style":352},[89,376,378],{"x":324,"y":377,"fill":86,"style":108,"textAnchor":326},"135.5","height \u002F width \u002F margin",[66,380],{"x":330,"y":141,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,382,383],{"x":334,"y":377,"fill":86,"style":361,"textAnchor":123},"yes, moves siblings",[66,385],{"x":338,"y":141,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,387,367],{"x":97,"y":377,"fill":86,"style":361,"textAnchor":123},[66,389],{"x":344,"y":141,"width":331,"height":321,"rx":70,"fill":129,"stroke":100,"style":130},[89,391,372],{"x":347,"y":377,"fill":86,"style":361,"textAnchor":123},[66,393],{"x":91,"y":394,"width":320,"height":321,"rx":70,"fill":85,"stroke":86,"style":352},"146.0",[89,396,47],{"x":324,"y":397,"fill":86,"style":108,"textAnchor":326},"165.5",[66,399],{"x":330,"y":394,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},"#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[89,403,404],{"x":334,"y":397,"fill":86,"style":361,"textAnchor":123},"no",[66,406],{"x":338,"y":394,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},[89,408,409],{"x":97,"y":397,"fill":86,"style":361,"textAnchor":123},"never",[66,411],{"x":344,"y":394,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},[89,413,414],{"x":347,"y":397,"fill":86,"style":361,"textAnchor":123},"composite only",[66,416],{"x":91,"y":417,"width":320,"height":321,"rx":70,"fill":85,"stroke":86,"style":352},"176.0",[89,419,421],{"x":324,"y":420,"fill":86,"style":108,"textAnchor":326},"195.5","opacity",[66,423],{"x":330,"y":417,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},[89,425,404],{"x":334,"y":420,"fill":86,"style":361,"textAnchor":123},[66,427],{"x":338,"y":417,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},[89,429,409],{"x":97,"y":420,"fill":86,"style":361,"textAnchor":123},[66,431],{"x":344,"y":417,"width":331,"height":321,"rx":70,"fill":400,"stroke":400,"style":401},[89,433,414],{"x":347,"y":420,"fill":86,"style":361,"textAnchor":123},[199,435,437],{"id":436},"step-by-step-resolution","Step-by-Step Resolution",[439,440,442],"h3",{"id":441},"_1-convert-positional-animations-to-transform","1. Convert positional animations to transform",[444,445,450],"pre",{"className":446,"code":447,"language":448,"meta":449,"style":449},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F* Before: animates layout every frame. *\u002F\n.toast { position: fixed; top: -80px; transition: top 300ms; }\n.toast.is-visible { top: 16px; }\n\n\u002F* After: final position is set once; only the composited transform animates. *\u002F\n.toast { position: fixed; top: 16px; transform: translateY(-120%); transition: transform 300ms; }\n.toast.is-visible { transform: translateY(0); }\n\u002F* trade-off: the element's layout box sits at its final position for the whole\n   animation, so hit-testing and focus see it there early. Add\n   pointer-events: none until visible if it overlaps interactive content. *\u002F\n","css","",[30,451,452,460,512,531,538,544,600,620,626,632],{"__ignoreMap":449},[453,454,456],"span",{"class":95,"line":455},1,[453,457,459],{"class":458},"sjfSM","\u002F* Before: animates layout every frame. *\u002F\n",[453,461,463,467,471,474,477,480,483,485,487,490,494,496,499,501,503,506,509],{"class":95,"line":462},2,[453,464,466],{"class":465},"sPXB4",".toast",[453,468,470],{"class":469},"saISM"," { ",[453,472,473],{"class":465},"position",[453,475,476],{"class":469},": ",[453,478,479],{"class":465},"fixed",[453,481,482],{"class":469},"; ",[453,484,40],{"class":465},[453,486,476],{"class":469},[453,488,489],{"class":465},"-80",[453,491,493],{"class":492},"sPARh","px",[453,495,482],{"class":469},[453,497,498],{"class":465},"transition",[453,500,476],{"class":469},[453,502,40],{"class":465},[453,504,505],{"class":465}," 300",[453,507,508],{"class":492},"ms",[453,510,511],{"class":469},"; }\n",[453,513,515,518,520,522,524,527,529],{"class":95,"line":514},3,[453,516,517],{"class":465},".toast.is-visible",[453,519,470],{"class":469},[453,521,40],{"class":465},[453,523,476],{"class":469},[453,525,526],{"class":465},"16",[453,528,493],{"class":492},[453,530,511],{"class":469},[453,532,534],{"class":95,"line":533},4,[453,535,537],{"emptyLinePlaceholder":536},true,"\n",[453,539,541],{"class":95,"line":540},5,[453,542,543],{"class":458},"\u002F* After: final position is set once; only the composited transform animates. *\u002F\n",[453,545,547,549,551,553,555,557,559,561,563,565,567,569,571,573,576,579,582,585,588,590,593,596,598],{"class":95,"line":546},6,[453,548,466],{"class":465},[453,550,470],{"class":469},[453,552,473],{"class":465},[453,554,476],{"class":469},[453,556,479],{"class":465},[453,558,482],{"class":469},[453,560,40],{"class":465},[453,562,476],{"class":469},[453,564,526],{"class":465},[453,566,493],{"class":492},[453,568,482],{"class":469},[453,570,47],{"class":465},[453,572,476],{"class":469},[453,574,575],{"class":465},"translateY",[453,577,578],{"class":469},"(",[453,580,581],{"class":465},"-120",[453,583,584],{"class":492},"%",[453,586,587],{"class":469},"); ",[453,589,498],{"class":465},[453,591,592],{"class":469},": transform ",[453,594,595],{"class":465},"300",[453,597,508],{"class":492},[453,599,511],{"class":469},[453,601,603,605,607,609,611,613,615,617],{"class":95,"line":602},7,[453,604,517],{"class":465},[453,606,470],{"class":469},[453,608,47],{"class":465},[453,610,476],{"class":469},[453,612,575],{"class":465},[453,614,578],{"class":469},[453,616,70],{"class":465},[453,618,619],{"class":469},"); }\n",[453,621,623],{"class":95,"line":622},8,[453,624,625],{"class":458},"\u002F* trade-off: the element's layout box sits at its final position for the whole\n",[453,627,629],{"class":95,"line":628},9,[453,630,631],{"class":458},"   animation, so hit-testing and focus see it there early. Add\n",[453,633,635],{"class":95,"line":634},10,[453,636,637],{"class":458},"   pointer-events: none until visible if it overlaps interactive content. *\u002F\n",[15,639,640],{},"Expected outcome: zero layout-shift entries from the animation and smoother motion, since compositor-only animations do not wait on the main thread.",[439,642,644],{"id":643},"_2-convert-carousel-slides-from-left-to-translatex","2. Convert carousel slides from left to translateX",[444,646,648],{"className":446,"code":647,"language":448,"meta":449,"style":449},".track { display: flex; transform: translateX(calc(var(--index) * -100%)); transition: transform 400ms ease; }\n.slide { flex: 0 0 100%; }\n@media (prefers-reduced-motion: reduce) { .track { transition: none; } }\n\u002F* trade-off: a transform-based track keeps all slides laid out, which costs\n   memory for many large slides. Virtualise beyond ~10 slides by keeping only\n   neighbours in the DOM. *\u002F\n",[30,649,650,718,741,762,767,772],{"__ignoreMap":449},[453,651,652,655,657,660,662,665,667,669,671,674,676,679,681,684,686,690,693,696,699,701,704,706,708,711,713,716],{"class":95,"line":455},[453,653,654],{"class":465},".track",[453,656,470],{"class":469},[453,658,659],{"class":465},"display",[453,661,476],{"class":469},[453,663,664],{"class":465},"flex",[453,666,482],{"class":469},[453,668,47],{"class":465},[453,670,476],{"class":469},[453,672,673],{"class":465},"translateX",[453,675,578],{"class":469},[453,677,678],{"class":465},"calc",[453,680,578],{"class":469},[453,682,683],{"class":465},"var",[453,685,578],{"class":469},[453,687,689],{"class":688},"sQw3B","--index",[453,691,692],{"class":469},") ",[453,694,695],{"class":492},"*",[453,697,698],{"class":465}," -100",[453,700,584],{"class":492},[453,702,703],{"class":469},")); ",[453,705,498],{"class":465},[453,707,592],{"class":469},[453,709,710],{"class":465},"400",[453,712,508],{"class":492},[453,714,715],{"class":465}," ease",[453,717,511],{"class":469},[453,719,720,723,725,727,729,731,734,737,739],{"class":95,"line":462},[453,721,722],{"class":465},".slide",[453,724,470],{"class":469},[453,726,664],{"class":465},[453,728,476],{"class":469},[453,730,70],{"class":465},[453,732,733],{"class":465}," 0",[453,735,736],{"class":465}," 100",[453,738,584],{"class":492},[453,740,511],{"class":469},[453,742,743,746,749,751,753,755,757,759],{"class":95,"line":514},[453,744,745],{"class":492},"@media",[453,747,748],{"class":469}," (prefers-reduced-motion: reduce) { ",[453,750,654],{"class":465},[453,752,470],{"class":469},[453,754,498],{"class":465},[453,756,476],{"class":469},[453,758,85],{"class":465},[453,760,761],{"class":469},"; } }\n",[453,763,764],{"class":95,"line":533},[453,765,766],{"class":458},"\u002F* trade-off: a transform-based track keeps all slides laid out, which costs\n",[453,768,769],{"class":95,"line":540},[453,770,771],{"class":458},"   memory for many large slides. Virtualise beyond ~10 slides by keeping only\n",[453,773,774],{"class":95,"line":546},[453,775,776],{"class":458},"   neighbours in the DOM. *\u002F\n",[15,778,779],{},"Expected outcome: autoplay carousels stop contributing to CLS entirely — often the single largest CLS fix on marketing homepages.",[439,781,783],{"id":782},"_3-animate-height-changes-with-the-flip-technique-or-grid-rows","3. Animate height changes with the FLIP technique or grid rows",[15,785,786],{},"Accordions genuinely change layout — content below must move. Make the movement shift-free by keeping it within the input exclusion window, or animate a visual proxy.",[444,788,790],{"className":446,"code":789,"language":448,"meta":449,"style":449},"\u002F* Animate grid-template-rows: the layout change happens, but in one step on click,\n   and the visual expansion is clipped by the container. Keep duration \u003C 500ms. *\u002F\n.panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 250ms ease; }\n.panel.is-open { grid-template-rows: 1fr; }\n.panel > .inner { overflow: hidden; }\n\u002F* trade-off: this still animates layout — each frame is real layout work —\n   but because it completes within 500ms of the click, the shifts are excluded.\n   Never trigger it from a timer or scroll, where no input excuses it. *\u002F\n",[30,791,792,797,802,844,861,883,888,893],{"__ignoreMap":449},[453,793,794],{"class":95,"line":455},[453,795,796],{"class":458},"\u002F* Animate grid-template-rows: the layout change happens, but in one step on click,\n",[453,798,799],{"class":95,"line":462},[453,800,801],{"class":458},"   and the visual expansion is clipped by the container. Keep duration \u003C 500ms. *\u002F\n",[453,803,804,807,809,811,813,816,818,821,823,825,828,830,832,835,838,840,842],{"class":95,"line":514},[453,805,806],{"class":465},".panel",[453,808,470],{"class":469},[453,810,659],{"class":465},[453,812,476],{"class":469},[453,814,815],{"class":465},"grid",[453,817,482],{"class":469},[453,819,820],{"class":465},"grid-template-rows",[453,822,476],{"class":469},[453,824,70],{"class":465},[453,826,827],{"class":492},"fr",[453,829,482],{"class":469},[453,831,498],{"class":465},[453,833,834],{"class":469},": grid-template-rows ",[453,836,837],{"class":465},"250",[453,839,508],{"class":492},[453,841,715],{"class":465},[453,843,511],{"class":469},[453,845,846,849,851,853,855,857,859],{"class":95,"line":533},[453,847,848],{"class":465},".panel.is-open",[453,850,470],{"class":469},[453,852,820],{"class":465},[453,854,476],{"class":469},[453,856,81],{"class":465},[453,858,827],{"class":492},[453,860,511],{"class":469},[453,862,863,865,868,871,873,876,878,881],{"class":95,"line":540},[453,864,806],{"class":465},[453,866,867],{"class":492}," >",[453,869,870],{"class":465}," .inner",[453,872,470],{"class":469},[453,874,875],{"class":465},"overflow",[453,877,476],{"class":469},[453,879,880],{"class":465},"hidden",[453,882,511],{"class":469},[453,884,885],{"class":95,"line":546},[453,886,887],{"class":458},"\u002F* trade-off: this still animates layout — each frame is real layout work —\n",[453,889,890],{"class":95,"line":602},[453,891,892],{"class":458},"   but because it completes within 500ms of the click, the shifts are excluded.\n",[453,894,895],{"class":95,"line":622},[453,896,897],{"class":458},"   Never trigger it from a timer or scroll, where no input excuses it. *\u002F\n",[15,899,900],{},"Expected outcome: user-triggered accordions remain excluded from CLS because the animation completes within the exclusion window.",[439,902,904],{"id":903},"_4-make-scroll-triggered-reveals-transform-only","4. Make scroll-triggered reveals transform-only",[15,906,907],{},"Reveal-on-scroll effects should fade and translate from their final layout position, never collapse and expand.",[444,909,911],{"className":446,"code":910,"language":448,"meta":449,"style":449},".reveal { opacity: 0; transform: translateY(24px); transition: opacity 400ms, transform 400ms; }\n.reveal.in-view { opacity: 1; transform: none; }\n\u002F* trade-off: content starting at opacity 0 is invisible to LCP until revealed.\n   Never apply reveal effects to above-the-fold content or the LCP element. *\u002F\n",[30,912,913,961,984,989],{"__ignoreMap":449},[453,914,915,918,920,922,924,926,928,930,932,934,936,939,941,943,945,948,950,952,955,957,959],{"class":95,"line":455},[453,916,917],{"class":465},".reveal",[453,919,470],{"class":469},[453,921,421],{"class":465},[453,923,476],{"class":469},[453,925,70],{"class":465},[453,927,482],{"class":469},[453,929,47],{"class":465},[453,931,476],{"class":469},[453,933,575],{"class":465},[453,935,578],{"class":469},[453,937,938],{"class":465},"24",[453,940,493],{"class":492},[453,942,587],{"class":469},[453,944,498],{"class":465},[453,946,947],{"class":469},": opacity ",[453,949,710],{"class":465},[453,951,508],{"class":492},[453,953,954],{"class":469},", transform ",[453,956,710],{"class":465},[453,958,508],{"class":492},[453,960,511],{"class":469},[453,962,963,966,968,970,972,974,976,978,980,982],{"class":95,"line":462},[453,964,965],{"class":465},".reveal.in-view",[453,967,470],{"class":469},[453,969,421],{"class":465},[453,971,476],{"class":469},[453,973,81],{"class":465},[453,975,482],{"class":469},[453,977,47],{"class":465},[453,979,476],{"class":469},[453,981,85],{"class":465},[453,983,511],{"class":469},[453,985,986],{"class":95,"line":514},[453,987,988],{"class":458},"\u002F* trade-off: content starting at opacity 0 is invisible to LCP until revealed.\n",[453,990,991],{"class":95,"line":533},[453,992,993],{"class":458},"   Never apply reveal effects to above-the-fold content or the LCP element. *\u002F\n",[15,995,996,997,222],{},"Expected outcome: scroll reveals no longer contribute to CLS; with modern CSS this can even be done with ",[19,998,1000],{"href":999},"\u002Frendering-css-performance\u002Fanimation-and-compositing-performance\u002Fscroll-driven-animations-without-javascript\u002F","scroll-driven animations without JavaScript",[15,1002,1003],{},[57,1004,64,1007,64,1010,64,1013,64,1015,64,1018,64,1020,64,1025,64,1032,64,1038,64,1043,64,1047,64,1052,64,1056,64,1060,64,1064,64,1068,64,1072,64,1076,64,1079,64],{"viewBox":1005,"width":60,"role":61,"ariaLabel":1006,"style":63},"0 0 760 216","Bar chart of CLS contributed by three animated components before and after converting them to transform animations.",[66,1008],{"className":1009,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],[73,1011,1012],{},"CLS from a marketing homepage, per animated component",[77,1014,1006],{},[66,1016],{"x":81,"y":81,"width":82,"height":1017,"rx":84,"fill":85,"stroke":86,"style":87},"214",[89,1019,1012],{"x":91,"y":92,"fill":86,"style":93},[95,1021],{"x1":1022,"y1":98,"x2":1022,"y2":1023,"stroke":100,"strokeWidth":101,"strokeDashArray":1024,"style":104},"586.6","194.0",[103,103],[89,1026,1031],{"x":1027,"y":1028,"fill":86,"style":1029,"textAnchor":1030},"167.6","92.0","font-size:13px","end","Hero carousel (left)",[66,1033],{"x":1034,"y":113,"width":1035,"height":1036,"rx":116,"fill":129,"stroke":100,"style":1037},"179.6","488.4","19","fill-opacity:0.7;stroke-opacity:0.9",[89,1039,1042],{"x":1040,"y":1028,"fill":86,"style":1041},"674.0","font-size:12px;font-weight:600","0.12",[89,1044,1046],{"x":1027,"y":1045,"fill":86,"style":1029,"textAnchor":1030},"123.0","Promo ticker (margin)",[66,1048],{"x":1034,"y":1049,"width":1050,"height":1036,"rx":116,"fill":400,"stroke":400,"style":1051},"109.0","203.5","fill-opacity:0.55;stroke-opacity:0.9",[89,1053,1055],{"x":1054,"y":1045,"fill":86,"style":1041},"389.1","0.05",[89,1057,1059],{"x":1027,"y":1058,"fill":86,"style":1029,"textAnchor":1030},"154.0","Scroll reveals (height)",[66,1061],{"x":1034,"y":1062,"width":1063,"height":1036,"rx":116,"fill":400,"stroke":400,"style":1051},"140.0","162.8",[89,1065,1067],{"x":1066,"y":1058,"fill":86,"style":1041},"348.4","0.04",[89,1069,1071],{"x":1027,"y":1070,"fill":86,"style":1029,"textAnchor":1030},"185.0","All three after transform",[66,1073],{"x":1034,"y":1074,"width":1075,"height":1036,"rx":116,"fill":400,"stroke":400,"style":1051},"171.0","2.0",[89,1077,70],{"x":1078,"y":1070,"fill":86,"style":1041},"187.6",[89,1080,1081],{"x":1022,"y":194,"fill":195,"style":196,"textAnchor":123},"CLS 0.1",[199,1083,1085],{"id":1084},"verification","Verification",[15,1087,1088,1089,1092,1093,222],{},"Re-record with Layout Shift Regions enabled and let autoplay run for at least two cycles; no regions should flash. In the trace, the Layout Shifts track should be empty during animations, and the Main track should show no Layout events per frame — transform animations run on the compositor. In CI, a Lighthouse run with a 10-second observation will catch autoplay regressions: assert ",[30,1090,1091],{},"cumulative-layout-shift"," below ",[30,1094,1055],{},[199,1096,1098],{"id":1097},"animation-libraries-and-framework-transitions","Animation Libraries and Framework Transitions",[15,1100,1101],{},"Many animation shifts come from libraries rather than hand-written CSS, so audit their configuration.",[15,1103,1104,1107,1108,1111],{},[210,1105,1106],{},"Framer Motion and similar layout animation APIs."," The ",[30,1109,1110],{},"layout"," prop animates layout changes using transforms (the FLIP technique) — it measures before and after, then animates a transform between them. The layout change itself happens in one frame, so if it is not triggered by input, that single frame is still a shift. Layout animations are shift-free only for input-triggered changes.",[15,1113,1114,1123,1124,1126,1127,1130,1131,1133],{},[210,1115,1116,1117,23,1120,222],{},"Vue ",[30,1118,1119],{},"\u003CTransition>",[30,1121,1122],{},"\u003CTransitionGroup>"," Enter\u002Fleave transitions using ",[30,1125,36],{}," or ",[30,1128,1129],{},"max-height"," classes are common in component libraries. Replace with opacity and transform classes; for list reordering, ",[30,1132,1122],{}," uses transforms for moves, which is safe.",[15,1135,1136,1139,1140,1143,1144,1147,1148,1143,1150,1152,1153,1155],{},[210,1137,1138],{},"GSAP and Web Animations API."," Both can animate any property. Prefer ",[30,1141,1142],{},"x","\u002F",[30,1145,1146],{},"y"," (transform) over ",[30,1149,32],{},[30,1151,40],{}," in GSAP, and ",[30,1154,47],{}," keyframes in WAAPI. These run off the main thread when only transform and opacity are animated.",[15,1157,1158,1161,1162,1164],{},[210,1159,1160],{},"CSS-in-JS animation helpers."," Generated keyframes sometimes include layout properties for convenience presets (\"slideDown\" animating ",[30,1163,36],{},"). Inspect the generated CSS before trusting a preset.",[199,1166,1168],{"id":1167},"faq","FAQ",[1170,1171,1174,1178],"details",{"className":1172},[1173],"faq-item",[1175,1176,1177],"summary",{},"Does will-change: transform prevent layout shifts?",[15,1179,1180,1181,1184,1185,1187,1188,1191,1192,1194],{},"No. ",[30,1182,1183],{},"will-change"," is a hint about compositing; it does not change which properties are animated. Animating ",[30,1186,40],{}," with ",[30,1189,1190],{},"will-change: transform"," set is still a layout animation. The fix is to animate ",[30,1193,47],{}," itself.",[1170,1196,1198,1201],{"className":1197},[1173],[1175,1199,1200],{},"Do CSS transitions on hover count toward CLS?",[15,1202,1203,1204,1207],{},"Hover is not a discrete input for the exclusion rule, so a hover transition that changes layout can count. In practice hover effects usually change color, shadow or transform, which do not shift layout. A hover that grows a card's height and pushes neighbours is a CLS risk — use ",[30,1205,1206],{},"transform: scale()"," instead.",[1170,1209,1211,1214],{"className":1210},[1173],[1175,1212,1213],{},"What about animating font-size or letter-spacing for emphasis?",[15,1215,1216,1217,1219,1220,1223],{},"Both change text metrics and therefore layout: the text box grows, lines rewrap, and everything after it moves. Emphasis animations on headings or numbers (\"count-up\" statistics that change width as digits change) are a frequent hidden source of small shifts. Use ",[30,1218,1206],{}," for emphasis, and give animated numbers a fixed width with ",[30,1221,1222],{},"font-variant-numeric: tabular-nums"," so changing digits never change the box size.",[199,1225,1227],{"id":1226},"related","Related",[204,1229,1230,1237,1244],{},[207,1231,1232,1236],{},[19,1233,1235],{"href":1234},"\u002Frendering-css-performance\u002Fanimation-and-compositing-performance\u002Fanimating-only-transform-and-opacity\u002F","Animating only transform and opacity"," — the rendering-performance side of the same rule.",[207,1238,1239,1243],{},[19,1240,1242],{"href":1241},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-cookie-consent-banners\u002F","Fixing CLS from cookie consent banners"," — entrance animations on overlays.",[207,1245,1246,1250],{},[19,1247,1249],{"href":1248},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffinding-layout-shift-sources-in-devtools\u002F","Finding layout shift sources in DevTools"," — confirming the animation is the cause.",[1252,1253,1255],"script",{"type":1254},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Fix CLS from Animations on top, left, height and margin\",\n  \"description\": \"Why animations on layout properties generate layout shifts, which ones are excused, and how to rewrite them with transforms.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Convert positional animations to transform\",\n      \"text\": \"\u002F After: final position is set once; only the composited transform animates.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Convert carousel slides from left to translateX\",\n      \"text\": \"Expected outcome: autoplay carousels stop contributing to CLS entirely — often the single largest CLS fix on marketing homepages.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Animate height changes with the FLIP technique or grid rows\",\n      \"text\": \"Accordions genuinely change layout — content below must move.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Make scroll-triggered reveals transform-only\",\n      \"text\": \"Reveal-on-scroll effects should fade and translate from their final layout position, never collapse and expand.\"\n    }\n  ]\n}\n",[1252,1257,1258],{"type":1254},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Fix CLS from Animations on top, left, height and margin\",\n  \"description\": \"Why animations on layout properties generate layout shifts, which ones are excused, and how to rewrite them with transforms.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties\u002F\"\n  }\n}\n",[1252,1260,1261],{"type":1254},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Core Web Vitals & Measurement\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Reducing Cumulative Layout Shift (CLS)\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Fixing CLS from Animating Layout Properties\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties\u002F\"\n    }\n  ]\n}\n",[1263,1264,1265],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}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 .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}",{"title":449,"searchDepth":462,"depth":462,"links":1267},[1268,1269,1270,1276,1277,1278,1279],{"id":201,"depth":462,"text":202},{"id":244,"depth":462,"text":245},{"id":436,"depth":462,"text":437,"children":1271},[1272,1273,1274,1275],{"id":441,"depth":514,"text":442},{"id":643,"depth":514,"text":644},{"id":782,"depth":514,"text":783},{"id":903,"depth":514,"text":904},{"id":1084,"depth":462,"text":1085},{"id":1097,"depth":462,"text":1098},{"id":1167,"depth":462,"text":1168},{"id":1226,"depth":462,"text":1227},"Why animations on layout properties generate layout shifts, which ones are excused, and how to rewrite them with transforms.","md",{"slug":1283,"type":1284,"breadcrumb":1285,"datePublished":1292,"dateModified":1292},"fixing-cls-from-animating-layout-properties","article",[1286,1288,1289,1290],{"name":1287,"url":1143},"Home",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1291},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties\u002F","2026-10-06","\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties",{"title":1295,"description":1296},"Fixing CLS from Animating top, height and margin","Animations on top, left, height or margin shift layout every frame and count toward CLS. Convert them to transform and opacity to keep CLS under 0.1.","core-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-animating-layout-properties\u002Findex","6j8Y5TNGwc3yZ_oS3QbKsX1nKaWqNwvufMgCaK7M_wI",[1300,1304],{"title":1301,"path":1302,"stem":1303},"Finding Layout Shift Sources in DevTools","\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffinding-layout-shift-sources-in-devtools","core-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffinding-layout-shift-sources-in-devtools\u002Findex",{"title":1305,"path":1306,"stem":1307},"Fixing CLS from Cookie Consent Banners","\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-cookie-consent-banners","core-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-cookie-consent-banners\u002Findex",1791308078733]