[{"data":1,"prerenderedAt":1291},["ShallowReactive",2],{"content:\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility":3,"surroundings:\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility":1282},{"id":4,"title":5,"body":6,"description":1262,"extension":1263,"meta":1264,"navigation":1275,"path":1276,"seo":1277,"stem":1280,"__hash__":1281},"content\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002Findex.md","CSS Containment & content-visibility",{"type":7,"value":8,"toc":1239},"minimark",[9,14,24,44,58,157,162,178,185,189,202,206,209,374,378,381,385,395,399,417,526,648,652,664,667,671,680,690,696,702,710,714,717,769,773,783,861,865,874,878,889,893,910,922,967,971,982,986,989,993,1022,1026,1033,1037,1069,1073,1085,1097,1112,1121,1130,1139,1152,1161,1170,1174,1197,1201,1224,1229,1232,1235],[10,11,13],"h1",{"id":12},"css-containment-content-visibility-rendering-less-less-often","CSS Containment & content-visibility: Rendering Less, Less Often",[15,16,17,18,23],"p",{},"This topic is part of ",[19,20,22],"a",{"href":21},"\u002Frendering-css-performance\u002F","Rendering & CSS Performance",". Browsers are conservative about rendering because CSS is global: a change to one element's size can, in principle, move every element after it, change the height of the page, and alter what is painted anywhere. When something changes, the browser must work out how far the effect reaches, and on large, complex pages that analysis — and the resulting style, layout and paint work — can be expensive.",[15,25,26,27,31,32,35,36,39,40,43],{},"CSS containment lets authors promise that parts of the page are independent. With ",[28,29,30],"code",{},"contain: layout",", an element's internal layout cannot affect anything outside it. With ",[28,33,34],{},"contain: paint",", its contents do not paint outside its box. With ",[28,37,38],{},"contain: size",", its size does not depend on its children. With ",[28,41,42],{},"contain: style",", counters and quotes inside do not leak out. Given these promises, the browser can limit work to the subtree that changed.",[15,45,46,49,50,53,54,57],{},[28,47,48],{},"content-visibility: auto"," goes a step further: for elements that are offscreen, the browser can skip style, layout and paint of their contents entirely, treating them as a box of a placeholder size. On long pages — articles, documentation, feeds, comment threads, product listings — that can reduce initial rendering work dramatically. The companion property ",[28,51,52],{},"contain-intrinsic-size"," supplies the placeholder size, and with the ",[28,55,56],{},"auto"," keyword the browser remembers the last rendered size, keeping scrollbars stable.",[15,59,60],{},[61,62,68,69,68,76,68,80,68,83,68,92,68,98,68,106,68,112,68,117,68,120,68,123,68,126,68,130,68,133,68,136,68,139,68,142,68,145,68,151,68,154,68],"svg",{"viewBox":63,"width":64,"role":65,"ariaLabel":66,"style":67},"0 0 760 318","100%","img","The CSS containment values and the guarantees each gives the browser.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[70,71],"rect",{"className":72,"x":74,"y":74,"width":64,"height":64,"fill":75},[73],"svg-canvas","0","#ffffff",[77,78,79],"title",{},"Containment types and what they promise",[81,82,66],"desc",{},[70,84],{"x":85,"y":85,"width":86,"height":87,"rx":88,"fill":89,"stroke":90,"style":91},"1","758","316","10","none","currentColor","stroke-opacity:0.18",[93,94,79],"text",{"x":95,"y":96,"fill":90,"style":97},"28.0","34.0","font-size:16px;font-weight:700",[70,99],{"x":95,"y":100,"width":101,"height":102,"rx":103,"fill":104,"stroke":104,"style":105},"56.0","704.0","40.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[93,107,30],{"x":108,"y":109,"fill":90,"style":110,"textAnchor":111},"42.0","80.5","font-size:13px;font-weight:700","start",[93,113,116],{"x":114,"y":109,"fill":90,"style":115,"textAnchor":111},"195.3","font-size:12px","Inside layout does not affect outside",[70,118],{"x":95,"y":119,"width":101,"height":102,"rx":103,"fill":104,"stroke":104,"style":105},"106.0",[93,121,34],{"x":108,"y":122,"fill":90,"style":110,"textAnchor":111},"130.5",[93,124,125],{"x":114,"y":122,"fill":90,"style":115,"textAnchor":111},"Contents never paint outside the box",[70,127],{"x":95,"y":128,"width":101,"height":102,"rx":103,"fill":90,"stroke":90,"style":129},"156.0","fill-opacity:0.06;stroke-opacity:0.4",[93,131,38],{"x":108,"y":132,"fill":90,"style":110,"textAnchor":111},"180.5",[93,134,135],{"x":114,"y":132,"fill":90,"style":115,"textAnchor":111},"Size does not depend on children",[70,137],{"x":95,"y":138,"width":101,"height":102,"rx":103,"fill":90,"stroke":90,"style":129},"206.0",[93,140,42],{"x":108,"y":141,"fill":90,"style":110,"textAnchor":111},"230.5",[93,143,144],{"x":114,"y":141,"fill":90,"style":115,"textAnchor":111},"Counters and quotes stay inside",[70,146],{"x":95,"y":147,"width":101,"height":102,"rx":103,"fill":148,"stroke":149,"style":150},"256.0","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[93,152,48],{"x":108,"y":153,"fill":90,"style":110,"textAnchor":111},"280.5",[93,155,156],{"x":114,"y":153,"fill":90,"style":115,"textAnchor":111},"Skip rendering contents while offscreen",[158,159,161],"h2",{"id":160},"the-metric-degradation-this-topic-addresses","The Metric Degradation This Topic Addresses",[15,163,164,165,169,170,173,174,177],{},"Rendering cost on large pages shows up in several places. On load, the first layout of a long page delays ",[166,167,168],"strong",{},"LCP"," render time and increases ",[166,171,172],{},"Total Blocking Time"," as the browser lays out content nobody can see yet. During interaction, every change that invalidates layout — expanding a comment, updating a counter, opening a menu — can trigger work across the whole document, adding to the presentation delay of ",[166,175,176],{},"INP",". On pages with frequent updates (live scores, dashboards, chat), small changes in one widget may repeatedly re-layout unrelated parts of the page.",[15,179,180,181,184],{},"In the Performance panel, these appear as large Layout and Recalculate Style events, sometimes covering thousands of nodes for a tiny change. The \"Layout\" event details show how many nodes needed layout and the root of the layout; a root of ",[28,182,183],{},"#document"," for a small widget update is a sign that containment could help.",[158,186,188],{"id":187},"prerequisites","Prerequisites",[190,191,192,196,199],"ul",{},[193,194,195],"li",{},"A Performance panel recording of page load and representative interactions, with CPU throttling.",[193,197,198],{},"An understanding of the page's structure: which regions are long and offscreen, which widgets update independently.",[193,200,201],{},"Visual regression testing or careful manual checks, since containment can change rendering (clipping, sizing).",[158,203,205],{"id":204},"_1-environment-setup-profile-rendering-work","1. Environment Setup: Profile Rendering Work",[15,207,208],{},"Record a load with 4x CPU throttling. Note total time in Layout and Recalculate Style, the number of nodes affected, and the layout roots. Then record common interactions and inspect the same events. Use the Rendering drawer's \"Layout Shift Regions\" and \"Paint flashing\" to see what repaints.",[210,211,216],"pre",{"className":212,"code":213,"language":214,"meta":215,"style":215},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Count elements and estimate offscreen share at load.\nconst all = document.querySelectorAll('body *').length;\nconst vh = innerHeight;\nconst offscreen = [...document.querySelectorAll('main > section, article > section')]\n  .filter((s) => s.getBoundingClientRect().top > vh * 2).length;\nconsole.log({ all, offscreenSections: offscreen });\n\u002F\u002F trade-off: getBoundingClientRect forces layout; run this only as a diagnostic.\n","javascript","",[28,217,218,227,265,278,307,356,368],{"__ignoreMap":215},[219,220,223],"span",{"class":221,"line":222},"line",1,[219,224,226],{"class":225},"sjfSM","\u002F\u002F Count elements and estimate offscreen share at load.\n",[219,228,230,234,238,241,245,249,252,256,259,262],{"class":221,"line":229},2,[219,231,233],{"class":232},"sPARh","const",[219,235,237],{"class":236},"sPXB4"," all",[219,239,240],{"class":232}," =",[219,242,244],{"class":243},"saISM"," document.",[219,246,248],{"class":247},"smZ65","querySelectorAll",[219,250,251],{"class":243},"(",[219,253,255],{"class":254},"sZ8jY","'body *'",[219,257,258],{"class":243},").",[219,260,261],{"class":236},"length",[219,263,264],{"class":243},";\n",[219,266,268,270,273,275],{"class":221,"line":267},3,[219,269,233],{"class":232},[219,271,272],{"class":236}," vh",[219,274,240],{"class":232},[219,276,277],{"class":243}," innerHeight;\n",[219,279,281,283,286,288,291,294,297,299,301,304],{"class":221,"line":280},4,[219,282,233],{"class":232},[219,284,285],{"class":236}," offscreen",[219,287,240],{"class":232},[219,289,290],{"class":243}," [",[219,292,293],{"class":232},"...",[219,295,296],{"class":243},"document.",[219,298,248],{"class":247},[219,300,251],{"class":243},[219,302,303],{"class":254},"'main > section, article > section'",[219,305,306],{"class":243},")]\n",[219,308,310,313,316,319,323,326,329,332,335,338,341,344,347,350,352,354],{"class":221,"line":309},5,[219,311,312],{"class":243},"  .",[219,314,315],{"class":247},"filter",[219,317,318],{"class":243},"((",[219,320,322],{"class":321},"sQw3B","s",[219,324,325],{"class":243},") ",[219,327,328],{"class":232},"=>",[219,330,331],{"class":243}," s.",[219,333,334],{"class":247},"getBoundingClientRect",[219,336,337],{"class":243},"().top ",[219,339,340],{"class":232},">",[219,342,343],{"class":243}," vh ",[219,345,346],{"class":232},"*",[219,348,349],{"class":236}," 2",[219,351,258],{"class":243},[219,353,261],{"class":236},[219,355,264],{"class":243},[219,357,359,362,365],{"class":221,"line":358},6,[219,360,361],{"class":243},"console.",[219,363,364],{"class":247},"log",[219,366,367],{"class":243},"({ all, offscreenSections: offscreen });\n",[219,369,371],{"class":221,"line":370},7,[219,372,373],{"class":225},"\u002F\u002F trade-off: getBoundingClientRect forces layout; run this only as a diagnostic.\n",[158,375,377],{"id":376},"_2-capture-a-baseline","2. Capture a Baseline",[15,379,380],{},"Record the rendering time on load (style + layout + paint) and for two or three interactions, on a mid-range device profile. In the field, segment INP and LCP by template.",[158,382,384],{"id":383},"_3-isolate-independent-regions","3. Isolate Independent Regions",[15,386,387,388,390,391,394],{},"Identify regions that are independent and either offscreen at load (long article sections, feed items, footers, comment lists) or updated frequently in isolation (widgets, sidebars, live counters, chat panels). These are the candidates for ",[28,389,48],{}," and ",[28,392,393],{},"contain"," respectively.",[158,396,398],{"id":397},"_4-apply-containment-and-measure","4. Apply Containment and Measure",[15,400,401,402,404,405,408,409,412,413,416],{},"Add ",[28,403,48],{}," with ",[28,406,407],{},"contain-intrinsic-size: auto \u003Cestimate>"," to long offscreen sections, and ",[28,410,411],{},"contain: layout paint"," (or ",[28,414,415],{},"contain: content",", which combines layout, paint and style) to independent widgets. Re-profile and compare.",[210,418,422],{"className":419,"code":420,"language":421,"meta":215,"style":215},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast",".feed-item, .article-section, .comment-thread {\n  content-visibility: auto;\n  contain-intrinsic-size: auto 600px;\n}\n.live-score, .chat-panel, .ad-slot {\n  contain: content;           \u002F* layout + paint + style *\u002F\n}\n\u002F* trade-off: paint containment clips overflow (tooltips, dropdowns, shadows),\n   so check every contained widget for content that must escape its box. *\u002F\n","css",[28,423,424,443,455,472,477,494,510,514,520],{"__ignoreMap":215},[219,425,426,429,432,435,437,440],{"class":221,"line":222},[219,427,428],{"class":236},".feed-item",[219,430,431],{"class":243},", ",[219,433,434],{"class":236},".article-section",[219,436,431],{"class":243},[219,438,439],{"class":236},".comment-thread",[219,441,442],{"class":243}," {\n",[219,444,445,448,451,453],{"class":221,"line":229},[219,446,447],{"class":236},"  content-visibility",[219,449,450],{"class":243},": ",[219,452,56],{"class":236},[219,454,264],{"class":243},[219,456,457,460,462,464,467,470],{"class":221,"line":267},[219,458,459],{"class":236},"  contain-intrinsic-size",[219,461,450],{"class":243},[219,463,56],{"class":236},[219,465,466],{"class":236}," 600",[219,468,469],{"class":232},"px",[219,471,264],{"class":243},[219,473,474],{"class":221,"line":280},[219,475,476],{"class":243},"}\n",[219,478,479,482,484,487,489,492],{"class":221,"line":309},[219,480,481],{"class":236},".live-score",[219,483,431],{"class":243},[219,485,486],{"class":236},".chat-panel",[219,488,431],{"class":243},[219,490,491],{"class":236},".ad-slot",[219,493,442],{"class":243},[219,495,496,499,501,504,507],{"class":221,"line":358},[219,497,498],{"class":236},"  contain",[219,500,450],{"class":243},[219,502,503],{"class":236},"content",[219,505,506],{"class":243},";           ",[219,508,509],{"class":225},"\u002F* layout + paint + style *\u002F\n",[219,511,512],{"class":221,"line":370},[219,513,476],{"class":243},[219,515,517],{"class":221,"line":516},8,[219,518,519],{"class":225},"\u002F* trade-off: paint containment clips overflow (tooltips, dropdowns, shadows),\n",[219,521,523],{"class":221,"line":522},9,[219,524,525],{"class":225},"   so check every contained widget for content that must escape its box. *\u002F\n",[15,527,528],{},[61,529,68,532,68,535,68,538,68,540,68,557,68,560,68,562,68,566,68,572,68,576,68,580,68,583,68,587,68,590,68,593,68,597,68,600,68,603,68,606,68,610,68,613,68,615,68,618,68,622,68,625,68,628,68,636,68,640,68,644,68],{"viewBox":530,"width":64,"role":65,"ariaLabel":531,"style":67},"0 0 760 149","Workflow from profiling rendering work through identifying independent regions to applying containment and verifying.",[70,533],{"className":534,"x":74,"y":74,"width":64,"height":64,"fill":75},[73],[77,536,537],{},"Containment workflow",[81,539,531],{},[541,542,543],"defs",{},[544,545,552],"marker",{"id":546,"viewBox":547,"refX":548,"refY":549,"markerWidth":550,"markerHeight":550,"orient":551},"fa21b69084","0 0 10 10","9","5","7","auto-start-reverse",[553,554],"path",{"d":555,"fill":90,"style":556},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[70,558],{"x":85,"y":85,"width":86,"height":559,"rx":88,"fill":89,"stroke":90,"style":91},"147",[93,561,537],{"x":95,"y":96,"fill":90,"style":97},[70,563],{"x":95,"y":100,"width":564,"height":565,"rx":103,"fill":104,"stroke":104,"style":105},"108.8","71.0",[93,567,571],{"x":568,"y":569,"fill":90,"style":110,"textAnchor":570},"82.4","78.0","middle","Profile",[93,573,575],{"x":568,"y":574,"fill":90,"style":115,"textAnchor":570},"96.0","load +",[93,577,579],{"x":568,"y":578,"fill":90,"style":115,"textAnchor":570},"112.0","interactions",[70,581],{"x":582,"y":100,"width":564,"height":565,"rx":103,"fill":104,"stroke":104,"style":105},"176.8",[93,584,586],{"x":585,"y":569,"fill":90,"style":110,"textAnchor":570},"231.2","Baseline",[93,588,589],{"x":585,"y":574,"fill":90,"style":115,"textAnchor":570},"layout nodes, ms",[70,591],{"x":592,"y":100,"width":564,"height":565,"rx":103,"fill":104,"stroke":104,"style":105},"325.6",[93,594,596],{"x":595,"y":569,"fill":90,"style":110,"textAnchor":570},"380.0","Identify",[93,598,599],{"x":595,"y":574,"fill":90,"style":115,"textAnchor":570},"offscreen +",[93,601,602],{"x":595,"y":578,"fill":90,"style":115,"textAnchor":570},"independent",[70,604],{"x":605,"y":100,"width":564,"height":565,"rx":103,"fill":148,"stroke":149,"style":150},"474.4",[93,607,609],{"x":608,"y":569,"fill":90,"style":110,"textAnchor":570},"528.8","Apply",[93,611,612],{"x":608,"y":574,"fill":90,"style":115,"textAnchor":570},"content-visibility,",[93,614,393],{"x":608,"y":578,"fill":90,"style":115,"textAnchor":570},[70,616],{"x":617,"y":100,"width":564,"height":565,"rx":103,"fill":104,"stroke":104,"style":105},"623.2",[93,619,621],{"x":620,"y":569,"fill":90,"style":110,"textAnchor":570},"677.6","Verify",[93,623,624],{"x":620,"y":574,"fill":90,"style":115,"textAnchor":570},"visuals, scroll,",[93,626,627],{"x":620,"y":578,"fill":90,"style":115,"textAnchor":570},"a11y",[221,629],{"x1":630,"y1":631,"x2":632,"y2":631,"stroke":90,"strokeWidth":633,"style":634,"markerEnd":635},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa21b69084)",[221,637],{"x1":638,"y1":631,"x2":639,"y2":631,"stroke":90,"strokeWidth":633,"style":634,"markerEnd":635},"285.6","323.6",[221,641],{"x1":642,"y1":631,"x2":643,"y2":631,"stroke":90,"strokeWidth":633,"style":634,"markerEnd":635},"434.4","472.4",[221,645],{"x1":646,"y1":631,"x2":647,"y2":631,"stroke":90,"strokeWidth":633,"style":634,"markerEnd":635},"583.2","621.2",[158,649,651],{"id":650},"deconstructing-what-content-visibility-skips","Deconstructing What content-visibility Skips",[15,653,654,655,657,658,660,661,663],{},"When an element with ",[28,656,48],{}," is far from the viewport, the browser treats it as if it had size containment and skips its contents: no style recalculation for descendants, no layout, no paint. The element itself still takes up space — the ",[28,659,52],{}," placeholder — so the page's total height and scroll position remain roughly correct. As the element approaches the viewport, the browser renders its contents, and with ",[28,662,56],{}," in the intrinsic size, remembers the actual size for next time.",[15,665,666],{},"The contents remain in the DOM and the accessibility tree. Find-in-page, anchor navigation and focus still work: the browser renders a skipped element when it needs to (for example, when find-in-page matches inside it). This distinguishes it from virtualization, which removes elements from the DOM entirely. The trade-off is that the DOM still holds all elements, so memory and some costs (such as DOM queries) are unchanged; only rendering is skipped.",[158,668,670],{"id":669},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,672,673,676,677,679],{},[166,674,675],{},"Scrollbar jumps."," If intrinsic size estimates differ greatly from real sizes, the scrollbar thumb jumps as sections render. Use ",[28,678,56],{}," with a realistic estimate; see the dedicated guide.",[15,681,682,685,686,689],{},[166,683,684],{},"Sections near the fold."," Content just below the viewport is rendered almost immediately as the user scrolls; applying ",[28,687,688],{},"content-visibility"," there adds overhead without saving much. Start a screen or two below the fold.",[15,691,692,695],{},[166,693,694],{},"Sticky and fixed positioning."," Containment creates a containing block for fixed-position descendants under some values, and paint containment clips; sticky headers inside contained elements may behave differently. Test carefully.",[15,697,698,701],{},[166,699,700],{},"Layout-dependent JavaScript."," Scripts that measure skipped elements force them to render, cancelling the benefit and possibly causing forced layout.",[15,703,704,68,707,709],{},[166,705,706],{},"Browser support.",[28,708,688],{}," is supported in current Chromium, Firefox and Safari versions, but older versions ignore it, which is harmless.",[158,711,713],{"id":712},"validation-and-budgeting","Validation and Budgeting",[15,715,716],{},"Compare rendering time on load before and after; on long pages, reductions of 50–90% in initial layout time are common. Scroll through the whole page checking for visual glitches, scrollbar jumps and layout shifts (CLS should not increase). Test find-in-page, anchor links and keyboard navigation through skipped sections. Budget rendering time for key templates in lab runs, though DOM size is often the more stable budget metric.",[718,719,720,733],"table",{},[721,722,723],"thead",{},[724,725,726,730],"tr",{},[727,728,729],"th",{},"Check",[727,731,732],{},"Expected outcome",[734,735,736,745,753,761],"tbody",{},[724,737,738,742],{},[739,740,741],"td",{},"Initial layout time (long pages)",[739,743,744],{},"Large reduction",[724,746,747,750],{},[739,748,749],{},"CLS while scrolling",[739,751,752],{},"No increase",[724,754,755,758],{},[739,756,757],{},"Find-in-page and anchors",[739,759,760],{},"Work as before",[724,762,763,766],{},[739,764,765],{},"Interaction layout roots",[739,767,768],{},"Contained widget, not #document",[158,770,772],{"id":771},"worked-example-a-documentation-platform","Worked Example: A Documentation Platform",[15,774,775,776,779,780,782],{},"A documentation platform rendered API reference pages with up to 40,000 elements (long tables of parameters). On a mid-range phone, initial layout took 620ms and the page's INP for expanding a section was 380ms. The team applied ",[28,777,778],{},"content-visibility: auto; contain-intrinsic-size: auto 1200px"," to each top-level reference section, and ",[28,781,415],{}," to the collapsible blocks. Initial layout dropped to 140ms, LCP improved by 350ms, and expanding a section now laid out only that block, bringing INP to 160ms. A few anchor links landed slightly off at first because of size estimates; switching to per-section estimates from the build fixed it.",[15,784,785],{},[61,786,68,789,68,792,68,795,68,797,68,800,68,802,68,810,68,817,68,824,68,829,68,833,68,838,68,842,68,846,68,851,68,855,68],{"viewBox":787,"width":64,"role":65,"ariaLabel":788,"style":67},"0 0 760 185","Bar chart of initial layout time on a long reference page before and after content-visibility and containment.",[70,790],{"className":791,"x":74,"y":74,"width":64,"height":64,"fill":75},[73],[77,793,794],{},"Initial layout time on an API reference page",[81,796,788],{},[70,798],{"x":85,"y":85,"width":86,"height":799,"rx":88,"fill":89,"stroke":90,"style":91},"183",[93,801,794],{"x":95,"y":96,"fill":90,"style":97},[221,803],{"x1":804,"y1":805,"x2":804,"y2":806,"stroke":149,"strokeWidth":633,"strokeDashArray":807,"style":809},"361.9","72.0","163.0",[808,808],"4","stroke-opacity:0.9",[93,811,816],{"x":812,"y":813,"fill":90,"style":814,"textAnchor":815},"204.1","92.0","font-size:13px","end","Before",[70,818],{"x":819,"y":569,"width":820,"height":821,"rx":822,"fill":89,"stroke":90,"style":823},"216.1","451.9","19","3","fill-opacity:0.7;stroke-opacity:0.35",[93,825,828],{"x":826,"y":813,"fill":90,"style":827},"674.0","font-size:12px;font-weight:600","620ms",[93,830,832],{"x":812,"y":831,"fill":90,"style":814,"textAnchor":815},"123.0","content-visibility on sections",[70,834],{"x":819,"y":835,"width":836,"height":821,"rx":822,"fill":104,"stroke":104,"style":837},"109.0","123.9","fill-opacity:0.55;stroke-opacity:0.9",[93,839,841],{"x":840,"y":831,"fill":90,"style":827},"346.0","170ms",[93,843,845],{"x":812,"y":844,"fill":90,"style":814,"textAnchor":815},"154.0","+ contain on collapsible blocks",[70,847],{"x":819,"y":848,"width":849,"height":821,"rx":822,"fill":148,"stroke":149,"style":850},"140.0","102.0","fill-opacity:0.7;stroke-opacity:0.9",[93,852,854],{"x":853,"y":844,"fill":90,"style":827},"324.2","140ms",[93,856,860],{"x":804,"y":857,"fill":858,"style":859,"textAnchor":570},"68.0","#8a6608","font-size:12px;font-weight:700","target",[158,862,864],{"id":863},"containment-for-widgets-that-update-often","Containment for Widgets That Update Often",[15,866,867,868,870,871,873],{},"Pages with frequent updates — live tickers, timers, notification counts, chat — benefit from containment even when they are short. Without it, a text change in a counter may invalidate layout for its ancestors, and in some layouts, the whole page. With ",[28,869,415],{}," on the widget (and ideally fixed dimensions so ",[28,872,38],{}," can apply too), updates stay inside the widget. The Performance panel shows the layout root changing from the document to the widget, and the layout cost of each update dropping to near zero.",[158,875,877],{"id":876},"interaction-with-accessibility","Interaction with Accessibility",[15,879,880,881,884,885,888],{},"Because skipped content stays in the DOM, screen readers can still access it, and headings in skipped sections still appear in navigation lists. However, some assistive technology interactions may cause the browser to render skipped content, temporarily cancelling the performance benefit — which is the correct trade-off. Avoid using ",[28,882,883],{},"content-visibility: hidden"," (which hides content like ",[28,886,887],{},"display: none"," but preserves rendering state) as a replacement for proper hidden semantics; it is intended for cached views such as inactive tabs.",[158,890,892],{"id":891},"choosing-between-containment-content-visibility-and-virtualization","Choosing Between Containment, content-visibility and Virtualization",[15,894,895,896,900,901,905,906,909],{},"These three techniques overlap, and choosing well saves effort. Use ",[166,897,898],{},[28,899,393],{}," when content is on screen and changes often: it does not skip anything, it only limits how far each change spreads. Use ",[166,902,903],{},[28,904,48],{}," when a page is long and mostly static, and most content starts offscreen: it skips rendering but keeps everything in the DOM, so accessibility, find-in-page and SEO are unaffected. Use ",[166,907,908],{},"virtualization"," when the number of items is very large (thousands of rows) or unbounded (infinite feeds), so that even holding them in the DOM costs too much memory and slows DOM queries and style invalidation.",[15,911,912,913,915,916,918,919,921],{},"They also combine well. A virtualized table can contain each row so cell updates stay local. A long article can use ",[28,914,688],{}," for its sections and ",[28,917,415],{}," for an embedded interactive widget. A feed can apply ",[28,920,688],{}," to pages of items and switch to virtualization once the session has loaded more than a few hundred entries.",[718,923,924,934],{},[721,925,926],{},[724,927,928,931],{},[727,929,930],{},"Situation",[727,932,933],{},"Best fit",[734,935,936,943,951,959],{},[724,937,938,941],{},[739,939,940],{},"Long static article or docs page",[739,942,48],{},[724,944,945,948],{},[739,946,947],{},"Busy widget on screen (ticker, chat)",[739,949,950],{},"contain: content or strict",[724,952,953,956],{},[739,954,955],{},"Thousands of list rows",[739,957,958],{},"Virtualization",[724,960,961,964],{},[739,962,963],{},"Infinite feed",[739,965,966],{},"content-visibility per page, then virtualization",[158,968,970],{"id":969},"debugging-containment-problems","Debugging Containment Problems",[15,972,973,974,977,978,981],{},"When containment changes how something looks, the cause is almost always one of three guarantees being false. If an element collapsed, size containment applied to an element whose size depends on its children — remove ",[28,975,976],{},"size"," or set an explicit size. If something is clipped, paint containment is cutting off overflow — move the overflowing UI to the top layer or drop ",[28,979,980],{},"paint",". If a fixed-position child moved, layout or paint containment made the element its containing block — move the fixed element outside. DevTools shows containment in the Computed panel, so checking which value applies is the fastest first step.",[158,983,985],{"id":984},"containment-in-component-libraries","Containment in Component Libraries",[15,987,988],{},"Design systems can make containment the default for components that are always independent, such as cards with fixed media areas, toasts and self-contained panels. Document the guarantee each component makes, so product teams know not to place overflowing popovers inside them, and expose an opt-out for unusual layouts. Centralising the decision in the library spreads the benefit across every page without each team having to rediscover it.",[158,990,992],{"id":991},"a-rollout-plan","A Rollout Plan",[994,995,996,999,1008,1013,1016,1019],"ol",{},[193,997,998],{},"Profile the longest templates and identify section boundaries.",[193,1000,1001,1002,1004,1005,1007],{},"Apply ",[28,1003,48],{}," to sections beyond the second screen, with ",[28,1006,56],{}," intrinsic sizes.",[193,1009,401,1010,1012],{},[28,1011,415],{}," to frequently updating widgets and check for clipped overflow.",[193,1014,1015],{},"Run visual regression, scroll, find-in-page and anchor tests.",[193,1017,1018],{},"Release to a share of traffic, compare LCP, INP and CLS by template.",[193,1020,1021],{},"Expand to more templates once the patterns are validated.",[158,1023,1025],{"id":1024},"measuring-in-the-field","Measuring in the Field",[15,1027,1028,1029,1032],{},"Containment changes are hard to see in aggregate metrics because they affect specific templates and interactions. Segment field data by template, and for INP, by interaction target, using the Long Animation Frames API to separate style and layout time from script time. A drop in ",[28,1030,1031],{},"styleAndLayoutStart","-to-end duration for interactions on contained widgets is the most direct evidence that containment is working.",[158,1034,1036],{"id":1035},"common-pitfalls","Common Pitfalls",[190,1038,1039,1045,1051,1057,1063],{},[193,1040,1041,1044],{},[166,1042,1043],{},"Applying content-visibility above the fold."," No skipped work, only overhead.",[193,1046,1047,1050],{},[166,1048,1049],{},"No intrinsic size."," Sections collapse to zero height while skipped, causing scrollbar jumps and shifts.",[193,1052,1053,1056],{},[166,1054,1055],{},"Paint containment on widgets with dropdowns."," Menus and tooltips get clipped.",[193,1058,1059,1062],{},[166,1060,1061],{},"Measuring skipped elements in scripts."," Forces rendering and layout.",[193,1064,1065,1068],{},[166,1066,1067],{},"Expecting memory savings."," Elements remain in the DOM; use virtualization for memory.",[158,1070,1072],{"id":1071},"faq","FAQ",[1074,1075,1078,1082],"details",{"className":1076},[1077],"faq-item",[1079,1080,1081],"summary",{},"What does content-visibility: auto do?",[15,1083,1084],{},"It lets the browser skip rendering an element's contents (style, layout, paint) while the element is offscreen, rendering them when it approaches the viewport.",[1074,1086,1088,1091],{"className":1087},[1077],[1079,1089,1090],{},"Is content-visibility the same as virtualization?",[15,1092,1093,1094,1096],{},"No. Virtualization removes offscreen elements from the DOM. ",[28,1095,688],{}," keeps them in the DOM but skips rendering them. It is simpler and more accessible, but does not reduce memory or DOM size.",[1074,1098,1100,1103],{"className":1099},[1077],[1079,1101,1102],{},"Why does my scrollbar jump with content-visibility?",[15,1104,1105,1106,1108,1109,1111],{},"Because skipped sections use the placeholder size from ",[28,1107,52],{},". If the estimate is far from the real size, the page height changes as sections render. Use ",[28,1110,56],{}," and realistic estimates.",[1074,1113,1115,1118],{"className":1114},[1077],[1079,1116,1117],{},"Does contain: layout improve performance on its own?",[15,1119,1120],{},"It can, by limiting layout to the contained subtree when its contents change. The gain depends on how much of the page would otherwise be laid out.",[1074,1122,1124,1127],{"className":1123},[1077],[1079,1125,1126],{},"Is containment safe for SEO?",[15,1128,1129],{},"Yes. Content remains in the DOM and is rendered for crawlers that need it. It does not hide content.",[1074,1131,1133,1136],{"className":1132},[1077],[1079,1134,1135],{},"Can containment break layouts?",[15,1137,1138],{},"Yes, if the promise is false: size containment on elements whose size depends on content collapses them, and paint containment clips overflow. Apply it only where the guarantees hold.",[1074,1140,1142,1145],{"className":1141},[1077],[1079,1143,1144],{},"What is contain: content?",[15,1146,1147,1148,1151],{},"Shorthand for ",[28,1149,1150],{},"contain: layout paint style",". It is the most common choice for independent widgets, because it does not require a fixed size.",[1074,1153,1155,1158],{"className":1154},[1077],[1079,1156,1157],{},"Does content-visibility help INP as well as LCP?",[15,1159,1160],{},"Mostly LCP and load-time blocking, because it reduces the initial rendering work. It can also help INP on long pages, since interactions that invalidate layout have fewer rendered elements to process when offscreen sections are skipped.",[1074,1162,1164,1167],{"className":1163},[1077],[1079,1165,1166],{},"How do I test containment changes before shipping?",[15,1168,1169],{},"Combine a Performance panel comparison (layout time and layout roots) with visual regression screenshots of every affected template and state, plus manual checks of find-in-page, anchors and keyboard navigation.",[158,1171,1173],{"id":1172},"guides-in-this-topic","Guides in This Topic",[190,1175,1176,1183,1190],{},[193,1177,1178,1182],{},[19,1179,1181],{"href":1180},"\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002Fusing-content-visibility-auto-on-long-pages\u002F","Using content-visibility: auto on long pages"," — skip offscreen rendering safely.",[193,1184,1185,1189],{},[19,1186,1188],{"href":1187},"\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002Fcontain-intrinsic-size-to-stop-scrollbar-jumps\u002F","contain-intrinsic-size to stop scrollbar jumps"," — stable placeholders for skipped content.",[193,1191,1192,1196],{},[19,1193,1195],{"href":1194},"\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002Fisolating-widgets-with-css-contain\u002F","Isolating widgets with CSS contain"," — keep frequent updates local.",[158,1198,1200],{"id":1199},"related","Related",[190,1202,1203,1210,1217],{},[193,1204,1205,1209],{},[19,1206,1208],{"href":1207},"\u002Frendering-css-performance\u002Fdom-size-and-list-virtualization\u002F","DOM size & list virtualization"," — when skipping rendering is not enough.",[193,1211,1212,1216],{},[19,1213,1215],{"href":1214},"\u002Frendering-css-performance\u002Flayout-thrashing-and-forced-reflow\u002F","Layout thrashing & forced reflow"," — scripts that defeat containment.",[193,1218,1219,1223],{},[19,1220,1222],{"href":1221},"\u002Fimage-media-optimization\u002Flazy-loading-images-without-hurting-lcp\u002Flazy-loading-css-background-images\u002F","Lazy-loading CSS background images"," — content-visibility also defers backgrounds.",[1225,1226,1228],"script",{"type":1227},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"CSS Containment & content-visibility: Rendering Less, Less Often\",\n  \"description\": \"How CSS containment and content-visibility limit and skip rendering work, where they pay off, and how to avoid scrollbar jumps, clipping and accessibility issues.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Profile Rendering Work\",\n      \"text\": \"Record a load with 4x CPU throttling.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"Record the rendering time on load (style + layout + paint) and for two or three interactions, on a mid-range device profile.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate Independent Regions\",\n      \"text\": \"Identify regions that are independent and either offscreen at load (long article sections, feed items, footers, comment lists) or updated frequently in isolation (widgets, sidebars, live counters, chat panels).\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply Containment and Measure\",\n      \"text\": \"Add content-visibility: auto with contain-intrinsic-size: auto to long offscreen sections, and contain: layout paint (or contain: content, which combines layout, paint and style) to independent widgets.\"\n    }\n  ]\n}\n",[1225,1230,1231],{"type":1227},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"CSS Containment & content-visibility: Rendering Less, Less Often\",\n  \"description\": \"How CSS containment and content-visibility limit and skip rendering work, where they pay off, and how to avoid scrollbar jumps, clipping and accessibility issues.\",\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\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002F\"\n  }\n}\n",[1225,1233,1234],{"type":1227},"\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\": \"Rendering & CSS Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"CSS Containment & content-visibility\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002F\"\n    }\n  ]\n}\n",[1236,1237,1238],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}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 .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}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);}",{"title":215,"searchDepth":229,"depth":229,"links":1240},[1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257,1258,1259,1260,1261],{"id":160,"depth":229,"text":161},{"id":187,"depth":229,"text":188},{"id":204,"depth":229,"text":205},{"id":376,"depth":229,"text":377},{"id":383,"depth":229,"text":384},{"id":397,"depth":229,"text":398},{"id":650,"depth":229,"text":651},{"id":669,"depth":229,"text":670},{"id":712,"depth":229,"text":713},{"id":771,"depth":229,"text":772},{"id":863,"depth":229,"text":864},{"id":876,"depth":229,"text":877},{"id":891,"depth":229,"text":892},{"id":969,"depth":229,"text":970},{"id":984,"depth":229,"text":985},{"id":991,"depth":229,"text":992},{"id":1024,"depth":229,"text":1025},{"id":1035,"depth":229,"text":1036},{"id":1071,"depth":229,"text":1072},{"id":1172,"depth":229,"text":1173},{"id":1199,"depth":229,"text":1200},"How CSS containment and content-visibility limit and skip rendering work, where they pay off, and how to avoid scrollbar jumps, clipping and accessibility issues.","md",{"slug":1265,"type":1266,"breadcrumb":1267,"datePublished":1274,"dateModified":1274},"css-containment-and-content-visibility","topic",[1268,1271,1272],{"name":1269,"url":1270},"Home","\u002F",{"name":22,"url":21},{"name":5,"url":1273},"\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002F","2026-10-06",true,"\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility",{"title":1278,"description":1279},"CSS Containment and content-visibility for Faster Rendering","Use contain and content-visibility: auto to skip offscreen rendering and isolate widgets, cutting style, layout and paint time on long pages and complex apps.","rendering-css-performance\u002Fcss-containment-and-content-visibility\u002Findex","VMCv3Y1JdthEjwrIHKnHB2ZGy5cv7Oien5J5DsiAHgw",[1283,1287],{"title":1284,"path":1285,"stem":1286,"children":-1},"Runtime CSS-in-JS Performance Cost","\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fruntime-css-in-js-performance-cost","rendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fruntime-css-in-js-performance-cost\u002Findex",{"title":1288,"path":1289,"stem":1290,"children":-1},"contain-intrinsic-size to Stop Scrollbar Jumps","\u002Frendering-css-performance\u002Fcss-containment-and-content-visibility\u002Fcontain-intrinsic-size-to-stop-scrollbar-jumps","rendering-css-performance\u002Fcss-containment-and-content-visibility\u002Fcontain-intrinsic-size-to-stop-scrollbar-jumps\u002Findex",1791308072318]