[{"data":1,"prerenderedAt":1604},["ShallowReactive",2],{"page:\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts":3,"all-docs-nav":1361},{"id":4,"title":5,"body":6,"breadcrumb":1331,"dateModified":1340,"datePublished":1340,"description":1341,"extension":1342,"faq":1343,"meta":1355,"navigation":376,"path":1356,"seo":1357,"slug":12,"stem":1358,"type":1359,"__hash__":1360},"content\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts\u002Findex.md","Eliminating Layout Shift From Web Fonts",{"type":7,"value":8,"toc":1309},"minimark",[9,13,17,31,36,58,62,65,206,210,215,221,499,507,511,518,745,750,754,757,810,817,821,828,832,838,944,948,951,1052,1140,1152,1156,1210,1214,1224,1228,1232,1241,1245,1248,1252,1255,1259,1262,1266,1269,1273,1305],[10,11,5],"h1",{"id":12},"eliminating-layout-shift-from-web-fonts",[14,15,16],"p",{},"Once images have reserved boxes, the shift that usually remains on a static site is the font swap. The browser paints with a fallback face, the web font arrives, and every line of text re-flows — pushing headings, images and everything below them to new positions. It is invisible in a fast local build and obvious on a cold 3G connection.",[14,18,19,20,25,26,30],{},"The fix has two halves: make the swap arrive sooner, and make it change nothing about the line boxes when it does. This guide covers both, with the CSS to paste and the measurements from a 900-word article template. It sits under ",[21,22,24],"a",{"href":23},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002F","Cumulative Layout Shift Fixes for Static Sites"," and complements the delivery-side advice in ",[21,27,29],{"href":28},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002F","Font Loading Strategies for Static Sites",".",[32,33,35],"h2",{"id":34},"prerequisites","Prerequisites",[37,38,39,52,55],"ul",{},[40,41,42,43,47,48,30],"li",{},"Self-hosted font files, or at least the ability to add ",[44,45,46],"code",{},"@font-face"," rules of your own. Third-party stylesheets you cannot edit make overrides impossible — start with ",[21,49,51],{"href":50},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift\u002F","Self-Hosting Google Fonts to Eliminate Layout Shift",[40,53,54],{},"A browser that reports layout shifts: Chrome DevTools' Performance panel, or the Layout Instability script from the parent guide.",[40,56,57],{},"A baseline CLS measured on a throttled profile — an unthrottled localhost load often hides the swap entirely.",[32,59,61],{"id":60},"why-the-swap-moves-things","Why the Swap Moves Things",[14,63,64],{},"A font's metrics — ascent, descent, line gap, and the average advance width of its glyphs — determine both how tall each line box is and how many characters fit on a line. When the fallback and the web font disagree on either, the swap re-wraps the paragraph and changes its height. A 900-word article that loses one line per paragraph moves everything below the first paragraph by the height of a line, repeatedly, all the way down the page.",[66,67,68,202],"figure",{},[69,70,77,78,77,82,77,86,77,93],"svg",{"viewBox":71,"role":72,"ariaLabelledBy":73,"xmlns":76},"0 0 760 300","img",[74,75],"fnt-metrics-title","fnt-metrics-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[79,80,81],"title",{"id":74},"Font metrics that decide line box height",[83,84,85],"desc",{"id":75},"A diagram of one line of text showing the baseline, the ascent above it, the descent below it and the line gap. A comparison panel shows the fallback face with a taller ascent producing a 26 pixel line box while the web font produces a 24 pixel line box, a difference of two pixels per line.",[87,88],"rect",{"x":89,"y":89,"width":90,"height":91,"fill":92},"0","760","300","#ffffff",[94,95,97,98,97,106,97,117,97,125,97,131,97,135,97,138,97,144,97,148,97,153,97,157,97,165,97,171,97,176,97,181,97,186,97,190,97,194,97,198,77],"g",{"style":96},"font-family:system-ui, sans-serif;font-size:13px","\n    ",[99,100,105],"text",{"x":101,"y":102,"fill":103,"style":104},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Four numbers decide whether the swap is visible",[87,107],{"x":108,"y":109,"width":110,"height":111,"rx":112,"fill":113,"opacity":114,"stroke":115,"style":116},"50","60","330","170","10","#d9e2ef","0.3","#556071","stroke-width:1.5px",[118,119],"line",{"x1":120,"y1":121,"x2":122,"y2":121,"stroke":123,"style":124},"70","160","360","#1982c4","stroke-width:2px",[99,126,130],{"x":127,"y":128,"fill":103,"style":129},"80","152","font-size:20px;font-weight:700","Handgloves",[118,132],{"x1":120,"y1":133,"x2":122,"y2":133,"stroke":113,"style":134},"110","stroke-width:2px;stroke-dasharray:5 4",[118,136],{"x1":120,"y1":137,"x2":122,"y2":137,"stroke":113,"style":134},"182",[99,139,143],{"x":140,"y":141,"fill":115,"style":142},"240","104","font-size:12px","ascent",[99,145,147],{"x":140,"y":146,"fill":115,"style":142},"196","descent",[99,149,152],{"x":140,"y":150,"fill":151,"style":142},"176","#166a9e","baseline",[99,154,156],{"x":120,"y":155,"fill":115,"style":142},"216","line gap adds space between boxes",[87,158],{"x":159,"y":109,"width":160,"height":161,"rx":112,"fill":162,"opacity":163,"stroke":164,"style":116},"420","290","78","#ff595e","0.14","#d83b41",[99,166,170],{"x":167,"y":168,"fill":103,"style":169},"565","86","font-size:13px;font-weight:700;text-anchor:middle","Fallback: Arial",[99,172,175],{"x":167,"y":173,"fill":115,"style":174},"106","font-size:12px;text-anchor:middle","ascent 0.905 · line box 26 px",[99,177,180],{"x":167,"y":178,"fill":103,"style":179},"126","font-size:12px;font-weight:700;text-anchor:middle","38 lines → 988 px tall",[87,182],{"x":159,"y":128,"width":160,"height":161,"rx":112,"fill":183,"opacity":184,"stroke":185,"style":116},"#8ac926","0.18","#5a8a16",[99,187,189],{"x":167,"y":188,"fill":103,"style":169},"178","Web font: Inter",[99,191,193],{"x":167,"y":192,"fill":115,"style":174},"198","ascent 0.969 · line box 24 px",[99,195,197],{"x":167,"y":196,"fill":103,"style":179},"218","36 lines → 864 px tall",[99,199,201],{"x":108,"y":200,"fill":115,"style":142},"268","124 px of movement on one article — every element below the text shifts by that amount",[203,204,205],"figcaption",{},"The swap is only free when both faces produce the same line boxes. Overrides let you force that agreement without editing either font file.",[32,207,209],{"id":208},"the-recipe","The Recipe",[211,212,214],"h3",{"id":213},"_1-declare-a-metric-matched-fallback","1. Declare a metric-matched fallback",[14,216,217,218,220],{},"Create an ",[44,219,46],{}," that wraps a local system font and adjusts its metrics to match your web font:",[222,223,228],"pre",{"className":224,"code":225,"language":226,"meta":227,"style":227},"language-css shiki shiki-themes github-light github-dark","@font-face {\n  font-family: 'Inter Fallback';\n  src: local('Arial'), local('Helvetica Neue');\n  size-adjust: 107.4%;      \u002F* scales glyph advance widths *\u002F\n  ascent-override: 90.0%;   \u002F* space above the baseline *\u002F\n  descent-override: 22.4%;  \u002F* space below it *\u002F\n  line-gap-override: 0%;    \u002F* extra leading between boxes *\u002F\n}\n\n@font-face {\n  font-family: 'Inter';\n  src: url('\u002Ffonts\u002Finter-var.woff2') format('woff2-variations');\n  font-weight: 100 900;\n  font-display: swap;\n}\n\nbody { font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif; }\n","css","",[44,229,230,241,258,288,309,328,347,365,371,378,385,397,425,441,454,459,464],{"__ignoreMap":227},[231,232,234,237],"span",{"class":118,"line":233},1,[231,235,46],{"class":236},"szBVR",[231,238,240],{"class":239},"sVt8B"," {\n",[231,242,244,248,251,255],{"class":118,"line":243},2,[231,245,247],{"class":246},"sj4cs","  font-family",[231,249,250],{"class":239},": ",[231,252,254],{"class":253},"sZZnC","'Inter Fallback'",[231,256,257],{"class":239},";\n",[231,259,261,264,266,269,272,275,278,280,282,285],{"class":118,"line":260},3,[231,262,263],{"class":246},"  src",[231,265,250],{"class":239},[231,267,268],{"class":246},"local",[231,270,271],{"class":239},"(",[231,273,274],{"class":253},"'Arial'",[231,276,277],{"class":239},"), ",[231,279,268],{"class":246},[231,281,271],{"class":239},[231,283,284],{"class":253},"'Helvetica Neue'",[231,286,287],{"class":239},");\n",[231,289,291,294,296,299,302,305],{"class":118,"line":290},4,[231,292,293],{"class":246},"  size-adjust",[231,295,250],{"class":239},[231,297,298],{"class":246},"107.4",[231,300,301],{"class":236},"%",[231,303,304],{"class":239},";      ",[231,306,308],{"class":307},"sJ8bj","\u002F* scales glyph advance widths *\u002F\n",[231,310,312,315,317,320,322,325],{"class":118,"line":311},5,[231,313,314],{"class":246},"  ascent-override",[231,316,250],{"class":239},[231,318,319],{"class":246},"90.0",[231,321,301],{"class":236},[231,323,324],{"class":239},";   ",[231,326,327],{"class":307},"\u002F* space above the baseline *\u002F\n",[231,329,331,334,336,339,341,344],{"class":118,"line":330},6,[231,332,333],{"class":246},"  descent-override",[231,335,250],{"class":239},[231,337,338],{"class":246},"22.4",[231,340,301],{"class":236},[231,342,343],{"class":239},";  ",[231,345,346],{"class":307},"\u002F* space below it *\u002F\n",[231,348,350,353,355,357,359,362],{"class":118,"line":349},7,[231,351,352],{"class":246},"  line-gap-override",[231,354,250],{"class":239},[231,356,89],{"class":246},[231,358,301],{"class":236},[231,360,361],{"class":239},";    ",[231,363,364],{"class":307},"\u002F* extra leading between boxes *\u002F\n",[231,366,368],{"class":118,"line":367},8,[231,369,370],{"class":239},"}\n",[231,372,374],{"class":118,"line":373},9,[231,375,377],{"emptyLinePlaceholder":376},true,"\n",[231,379,381,383],{"class":118,"line":380},10,[231,382,46],{"class":236},[231,384,240],{"class":239},[231,386,388,390,392,395],{"class":118,"line":387},11,[231,389,247],{"class":246},[231,391,250],{"class":239},[231,393,394],{"class":253},"'Inter'",[231,396,257],{"class":239},[231,398,400,402,404,407,409,412,415,418,420,423],{"class":118,"line":399},12,[231,401,263],{"class":246},[231,403,250],{"class":239},[231,405,406],{"class":246},"url",[231,408,271],{"class":239},[231,410,411],{"class":253},"'\u002Ffonts\u002Finter-var.woff2'",[231,413,414],{"class":239},") ",[231,416,417],{"class":246},"format",[231,419,271],{"class":239},[231,421,422],{"class":253},"'woff2-variations'",[231,424,287],{"class":239},[231,426,428,431,433,436,439],{"class":118,"line":427},13,[231,429,430],{"class":246},"  font-weight",[231,432,250],{"class":239},[231,434,435],{"class":246},"100",[231,437,438],{"class":246}," 900",[231,440,257],{"class":239},[231,442,444,447,449,452],{"class":118,"line":443},14,[231,445,446],{"class":246},"  font-display",[231,448,250],{"class":239},[231,450,451],{"class":246},"swap",[231,453,257],{"class":239},[231,455,457],{"class":118,"line":456},15,[231,458,370],{"class":239},[231,460,462],{"class":118,"line":461},16,[231,463,377],{"emptyLinePlaceholder":376},[231,465,467,471,474,477,479,481,484,486,488,491,493,496],{"class":118,"line":466},17,[231,468,470],{"class":469},"s9eBZ","body",[231,472,473],{"class":239}," { ",[231,475,476],{"class":246},"font-family",[231,478,250],{"class":239},[231,480,394],{"class":253},[231,482,483],{"class":239},", ",[231,485,254],{"class":253},[231,487,483],{"class":239},[231,489,490],{"class":246},"system-ui",[231,492,483],{"class":239},[231,494,495],{"class":246},"sans-serif",[231,497,498],{"class":239},"; }\n",[14,500,501,502,506],{},"The fallback must come ",[503,504,505],"strong",{},"immediately after"," the web font in the stack — that is the face the browser uses during the swap window, and the only one whose metrics you can control.",[211,508,510],{"id":509},"_2-compute-the-override-values","2. Compute the override values",[14,512,513,514,517],{},"The four numbers come from the font's own metrics: divide the web font's ascent, descent and line gap by its units-per-em, and set ",[44,515,516],{},"size-adjust"," to the ratio of average character widths between the fallback and the web font. You can read them from the file directly:",[222,519,523],{"className":520,"code":521,"language":522,"meta":227,"style":227},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F node scripts\u002Ffont-metrics.mjs fonts\u002Finter-var.woff2\nimport { readFileSync } from 'node:fs';\nimport * as fontkit from 'fontkit';\n\nconst font = fontkit.create(readFileSync(process.argv[2]));\nconst upm = font.unitsPerEm;\nconsole.log('ascent-override :', (font.ascent \u002F upm * 100).toFixed(1) + '%');\nconsole.log('descent-override:', (Math.abs(font.descent) \u002F upm * 100).toFixed(1) + '%');\nconsole.log('line-gap-override:', (font.lineGap \u002F upm * 100).toFixed(1) + '%');\n","javascript",[44,524,525,530,546,566,570,602,614,663,707],{"__ignoreMap":227},[231,526,527],{"class":118,"line":233},[231,528,529],{"class":307},"\u002F\u002F node scripts\u002Ffont-metrics.mjs fonts\u002Finter-var.woff2\n",[231,531,532,535,538,541,544],{"class":118,"line":243},[231,533,534],{"class":236},"import",[231,536,537],{"class":239}," { readFileSync } ",[231,539,540],{"class":236},"from",[231,542,543],{"class":253}," 'node:fs'",[231,545,257],{"class":239},[231,547,548,550,553,556,559,561,564],{"class":118,"line":260},[231,549,534],{"class":236},[231,551,552],{"class":246}," *",[231,554,555],{"class":236}," as",[231,557,558],{"class":239}," fontkit ",[231,560,540],{"class":236},[231,562,563],{"class":253}," 'fontkit'",[231,565,257],{"class":239},[231,567,568],{"class":118,"line":290},[231,569,377],{"emptyLinePlaceholder":376},[231,571,572,575,578,581,584,588,590,593,596,599],{"class":118,"line":311},[231,573,574],{"class":236},"const",[231,576,577],{"class":246}," font",[231,579,580],{"class":236}," =",[231,582,583],{"class":239}," fontkit.",[231,585,587],{"class":586},"sScJk","create",[231,589,271],{"class":239},[231,591,592],{"class":586},"readFileSync",[231,594,595],{"class":239},"(process.argv[",[231,597,598],{"class":246},"2",[231,600,601],{"class":239},"]));\n",[231,603,604,606,609,611],{"class":118,"line":330},[231,605,574],{"class":236},[231,607,608],{"class":246}," upm",[231,610,580],{"class":236},[231,612,613],{"class":239}," font.unitsPerEm;\n",[231,615,616,619,622,624,627,630,633,636,639,642,645,648,650,653,655,658,661],{"class":118,"line":349},[231,617,618],{"class":239},"console.",[231,620,621],{"class":586},"log",[231,623,271],{"class":239},[231,625,626],{"class":253},"'ascent-override :'",[231,628,629],{"class":239},", (font.ascent ",[231,631,632],{"class":236},"\u002F",[231,634,635],{"class":239}," upm ",[231,637,638],{"class":236},"*",[231,640,641],{"class":246}," 100",[231,643,644],{"class":239},").",[231,646,647],{"class":586},"toFixed",[231,649,271],{"class":239},[231,651,652],{"class":246},"1",[231,654,414],{"class":239},[231,656,657],{"class":236},"+",[231,659,660],{"class":253}," '%'",[231,662,287],{"class":239},[231,664,665,667,669,671,674,677,680,683,685,687,689,691,693,695,697,699,701,703,705],{"class":118,"line":367},[231,666,618],{"class":239},[231,668,621],{"class":586},[231,670,271],{"class":239},[231,672,673],{"class":253},"'descent-override:'",[231,675,676],{"class":239},", (Math.",[231,678,679],{"class":586},"abs",[231,681,682],{"class":239},"(font.descent) ",[231,684,632],{"class":236},[231,686,635],{"class":239},[231,688,638],{"class":236},[231,690,641],{"class":246},[231,692,644],{"class":239},[231,694,647],{"class":586},[231,696,271],{"class":239},[231,698,652],{"class":246},[231,700,414],{"class":239},[231,702,657],{"class":236},[231,704,660],{"class":253},[231,706,287],{"class":239},[231,708,709,711,713,715,718,721,723,725,727,729,731,733,735,737,739,741,743],{"class":118,"line":373},[231,710,618],{"class":239},[231,712,621],{"class":586},[231,714,271],{"class":239},[231,716,717],{"class":253},"'line-gap-override:'",[231,719,720],{"class":239},", (font.lineGap ",[231,722,632],{"class":236},[231,724,635],{"class":239},[231,726,638],{"class":236},[231,728,641],{"class":246},[231,730,644],{"class":239},[231,732,647],{"class":586},[231,734,271],{"class":239},[231,736,652],{"class":246},[231,738,414],{"class":239},[231,740,657],{"class":236},[231,742,660],{"class":253},[231,744,287],{"class":239},[14,746,747,749],{},[44,748,516],{}," is the one value you tune rather than read: start at 100%, render a long paragraph in each face, and adjust until the two block heights match. Build-time tooling can do all of this automatically — the Fontaine plugin generates the fallback rules during the build for Vite-based generators, which keeps the numbers correct when you change typeface.",[211,751,753],{"id":752},"_3-preload-the-face-that-paints-first","3. Preload the face that paints first",[14,755,756],{},"Overrides make the swap invisible; preloading makes it early, which matters for perceived quality even at zero CLS:",[222,758,762],{"className":759,"code":760,"language":761,"meta":227,"style":227},"language-html shiki shiki-themes github-light github-dark","\u003Clink rel=\"preload\" href=\"\u002Ffonts\u002Finter-var.woff2\" as=\"font\" type=\"font\u002Fwoff2\" crossorigin>\n","html",[44,763,764],{"__ignoreMap":227},[231,765,766,769,772,775,778,781,784,786,789,791,793,796,799,801,804,807],{"class":118,"line":233},[231,767,768],{"class":239},"\u003C",[231,770,771],{"class":469},"link",[231,773,774],{"class":586}," rel",[231,776,777],{"class":239},"=",[231,779,780],{"class":253},"\"preload\"",[231,782,783],{"class":586}," href",[231,785,777],{"class":239},[231,787,788],{"class":253},"\"\u002Ffonts\u002Finter-var.woff2\"",[231,790,555],{"class":586},[231,792,777],{"class":239},[231,794,795],{"class":253},"\"font\"",[231,797,798],{"class":586}," type",[231,800,777],{"class":239},[231,802,803],{"class":253},"\"font\u002Fwoff2\"",[231,805,806],{"class":586}," crossorigin",[231,808,809],{"class":239},">\n",[14,811,812,813,30],{},"Preload only the file the first screen needs — usually one variable body face. Preloading four weights competes with the LCP image for bandwidth and slows the thing you actually want first, a trade-off covered in ",[21,814,816],{"href":815},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints\u002F","Optimizing LCP on Astro With Priority Hints",[211,818,820],{"id":819},"_4-subset-so-the-file-arrives-before-the-reader-notices","4. Subset so the file arrives before the reader notices",[14,822,823,824,30],{},"A full Latin-Extended variable font is 90-140 KB; subset to the characters you actually publish and it drops to 25-40 KB. The mechanics, including how to keep punctuation and symbols from disappearing, are in ",[21,825,827],{"href":826},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render\u002F","Subsetting Variable Fonts for Faster First Render",[211,829,831],{"id":830},"_5-consider-font-display-optional-for-body-text","5. Consider font-display: optional for body text",[14,833,834,837],{},[44,835,836],{},"optional"," gives the font a 100 ms window; if it misses, the page keeps the fallback for that visit and swaps nothing. This guarantees zero font CLS without any override maths, at the cost of readers occasionally seeing the fallback for a whole session. It is the right default for documentation, where the typeface is not the product.",[66,839,840,941],{},[69,841,77,846,77,849,77,852,77,855],{"viewBox":842,"role":72,"ariaLabelledBy":843,"xmlns":76},"0 0 760 280",[844,845],"fnt-display-title","fnt-display-desc",[79,847,848],{"id":844},"How font-display values divide the loading timeline",[83,850,851],{"id":845},"Three timelines over three seconds. With block the text is invisible for a three second block period before the font paints. With swap the fallback paints immediately and the font swaps in whenever it arrives, causing a shift. With optional the fallback paints immediately and the font is only used if it arrives within a 100 millisecond window, otherwise it is kept for the next visit.",[87,853],{"x":89,"y":89,"width":90,"height":854,"fill":92},"280",[94,856,97,858,97,862,97,868,97,874,97,879,97,882,97,886,97,889,97,893,97,897,97,902,97,905,97,908,97,911,97,917,97,921,97,925,97,929,97,933,97,937,77],{"style":857},"font-family:system-ui, sans-serif;font-size:12px",[99,859,861],{"x":101,"y":860,"fill":103,"style":104},"26","Three policies for the same slow font request",[99,863,867],{"x":864,"y":865,"fill":103,"style":866},"40","66","font-size:12px;font-weight:700","block",[87,869],{"x":870,"y":108,"width":871,"height":860,"rx":872,"fill":162,"opacity":873,"stroke":164,"style":116},"150","250","5","0.16",[99,875,878],{"x":876,"y":877,"fill":103,"style":174},"275","68","invisible text",[87,880],{"x":881,"y":108,"width":160,"height":860,"rx":872,"fill":183,"opacity":184,"stroke":185,"style":116},"410",[99,883,885],{"x":884,"y":877,"fill":103,"style":174},"555","web font · no shift, late text",[99,887,451],{"x":864,"y":888,"fill":103,"style":866},"136",[87,890],{"x":870,"y":891,"width":871,"height":860,"rx":872,"fill":113,"opacity":892,"stroke":115,"style":116},"120","0.55",[99,894,896],{"x":876,"y":895,"fill":103,"style":174},"138","fallback text",[87,898],{"x":881,"y":891,"width":160,"height":860,"rx":872,"fill":899,"opacity":900,"stroke":901,"style":116},"#ffca3a","0.34","#a97b00",[99,903,904],{"x":884,"y":895,"fill":103,"style":174},"web font · shift unless metrics match",[99,906,836],{"x":864,"y":907,"fill":103,"style":866},"206",[87,909],{"x":870,"y":910,"width":109,"height":860,"rx":872,"fill":123,"opacity":873,"stroke":123,"style":116},"190",[99,912,916],{"x":913,"y":914,"fill":103,"style":915},"180","208","font-size:11px;text-anchor:middle","100 ms",[87,918],{"x":919,"y":910,"width":920,"height":860,"rx":872,"fill":113,"opacity":892,"stroke":115,"style":116},"220","480",[99,922,924],{"x":923,"y":914,"fill":103,"style":174},"460","fallback kept for this visit · no shift at all",[118,926],{"x1":870,"y1":927,"x2":928,"y2":927,"stroke":115,"style":116},"232","700",[99,930,932],{"x":870,"y":871,"fill":115,"style":931},"font-size:11px","0 s",[99,934,936],{"x":935,"y":871,"fill":115,"style":931},"400","1.5 s",[99,938,940],{"x":939,"y":871,"fill":115,"style":931},"660","3 s",[203,942,943],{},"Every policy trades one cost for another: `block` delays text, `swap` risks a shift, `optional` risks not using your typeface at all. Metric overrides are what make `swap` cost nothing.",[32,945,947],{"id":946},"measured-impact","Measured Impact",[14,949,950],{},"Measured on a 900-word article template, Chrome 4× CPU throttle, fast 3G profile, median of five runs. The site self-hosts one variable face at 38 KB after subsetting:",[952,953,954,973],"table",{},[955,956,957],"thead",{},[958,959,960,964,967,970],"tr",{},[961,962,963],"th",{},"Configuration",[961,965,966],{},"CLS",[961,968,969],{},"Swap visible at",[961,971,972],{},"Notes",[974,975,976,992,1009,1023,1036],"tbody",{},[958,977,978,984,986,989],{},[979,980,981,982],"td",{},"Google Fonts stylesheet, ",[44,983,451],{},[979,985,163],{},[979,987,988],{},"1.42 s",[979,990,991],{},"Extra DNS + connection before the font request starts",[958,993,994,1000,1003,1006],{},[979,995,996,997,999],{},"Self-hosted, ",[44,998,451],{},", no overrides",[979,1001,1002],{},"0.11",[979,1004,1005],{},"0.98 s",[979,1007,1008],{},"Earlier swap, same reflow",[958,1010,1011,1014,1017,1020],{},[979,1012,1013],{},"Self-hosted + preload, no overrides",[979,1015,1016],{},"0.09",[979,1018,1019],{},"0.61 s",[979,1021,1022],{},"Shorter fallback window, shift unchanged in size",[958,1024,1025,1028,1031,1033],{},[979,1026,1027],{},"Self-hosted + preload + overrides",[979,1029,1030],{},"0.00",[979,1032,1019],{},[979,1034,1035],{},"Line boxes identical across the swap",[958,1037,1038,1044,1046,1049],{},[979,1039,1040,1041],{},"Self-hosted + ",[44,1042,1043],{},"font-display: optional",[979,1045,1030],{},[979,1047,1048],{},"n\u002Fa",[979,1050,1051],{},"No swap on a slow connection",[66,1053,1054,1137],{},[69,1055,77,1060,77,1063,77,1066,77,1068],{"viewBox":1056,"role":72,"ariaLabelledBy":1057,"xmlns":76},"0 0 760 290",[1058,1059],"fnt-bars-title","fnt-bars-desc",[79,1061,1062],{"id":1058},"CLS by font loading configuration",[83,1064,1065],{"id":1059},"A horizontal bar chart on a shared scale. Google Fonts with swap scores 0.14, self-hosted with swap scores 0.11, self-hosted with preload scores 0.09, and self-hosted with preload plus metric overrides scores 0.00. Only the last configuration crosses below the 0.10 good threshold marked by a dashed line.",[87,1067],{"x":89,"y":89,"width":90,"height":160,"fill":92},[94,1069,97,1070,97,1073,97,1077,97,1081,97,1087,97,1090,97,1094,97,1098,97,1101,97,1106,97,1110,97,1114,97,1118,97,1121,97,1125,97,1129,97,1133,77],{"style":857},[99,1071,1072],{"x":101,"y":860,"fill":103,"style":104},"Only the overrides take the score to zero",[118,1074],{"x1":160,"y1":1075,"x2":160,"y2":1076,"stroke":113,"style":116},"44","238",[118,1078],{"x1":1079,"y1":1075,"x2":1079,"y2":1076,"stroke":183,"style":1080},"580","stroke-width:2px;stroke-dasharray:6 5",[99,1082,1086],{"x":1083,"y":1084,"fill":185,"style":1085},"590","58","font-size:11px;font-weight:700","0.10",[99,1088,1089],{"x":109,"y":161,"fill":103,"style":866},"Google Fonts + swap",[87,1091],{"x":160,"y":1092,"width":1093,"height":102,"rx":872,"fill":162,"opacity":873,"stroke":164,"style":116},"64","406",[99,1095,163],{"x":1096,"y":1097,"fill":164,"style":179},"493","83",[99,1099,1100],{"x":109,"y":178,"fill":103,"style":866},"Self-hosted + swap",[87,1102],{"x":160,"y":1103,"width":1104,"height":102,"rx":872,"fill":162,"opacity":1105,"stroke":164,"style":116},"112","319","0.13",[99,1107,1002],{"x":1108,"y":1109,"fill":164,"style":179},"450","131",[99,1111,1113],{"x":109,"y":1112,"fill":103,"style":866},"174","+ preload",[87,1115],{"x":160,"y":121,"width":1116,"height":102,"rx":872,"fill":899,"opacity":1117,"stroke":901,"style":116},"261","0.4",[99,1119,1016],{"x":159,"y":1120,"fill":901,"style":179},"179",[99,1122,1124],{"x":109,"y":1123,"fill":103,"style":866},"222","+ metric overrides",[87,1126],{"x":160,"y":914,"width":1127,"height":102,"rx":1128,"fill":183,"opacity":1117,"stroke":185,"style":116},"6","3",[99,1130,1030],{"x":1131,"y":1132,"fill":185,"style":866},"322","227",[99,1134,1136],{"x":109,"y":1135,"fill":115,"style":931},"266","Shared linear scale · 900-word article, Chrome 4× CPU throttle, fast 3G, median of 5 runs",[203,1138,1139],{},"Self-hosting and preloading shorten the window the fallback is visible, which readers notice — but the score only collapses when the two faces produce identical line boxes.",[14,1141,1142,1143,1146,1147,1151],{},"The pattern is worth internalising: ",[503,1144,1145],{},"delivery changes move the swap earlier, metric changes make it free."," Teams often stop after preloading because the page ",[1148,1149,1150],"em",{},"feels"," better and are surprised the field CLS barely moves.",[32,1153,1155],{"id":1154},"pitfalls-rollback","Pitfalls & Rollback",[37,1157,1158,1164,1176,1189,1195,1201],{},[40,1159,1160,1163],{},[503,1161,1162],{},"One override for every face."," Body, headings and monospace each need their own fallback rule; a heading at 2.5 rem magnifies a 3% error into several pixels.",[40,1165,1166,1171,1172,1175],{},[503,1167,1168,1169,30],{},"Fallback listed after ",[44,1170,490],{}," The browser uses the ",[1148,1173,1174],{},"next"," available family during the swap. If a generic family sits between the web font and your adjusted fallback, your overrides never render.",[40,1177,1178,1184,1185,1188],{},[503,1179,1180,1183],{},[44,1181,1182],{},"local()"," that does not exist."," ",[44,1186,1187],{},"local('Arial')"," resolves on most desktops but not on many Linux and Android devices; list two or three candidates so the rule is not skipped entirely.",[40,1190,1191,1194],{},[503,1192,1193],{},"Testing on a warm cache."," After the first load the font is cached and swaps instantly — always measure with the cache disabled and CPU throttled.",[40,1196,1197,1200],{},[503,1198,1199],{},"Overrides on a third-party stylesheet."," You cannot adjust a face declared in a stylesheet you do not control; self-host first.",[40,1202,1203,1206,1207,1209],{},[503,1204,1205],{},"Rollback:"," the overrides are one ",[44,1208,46],{}," block and one family name in the font stack. Deleting them restores previous behaviour immediately, with no build or cache implications.",[32,1211,1213],{"id":1212},"conclusion","Conclusion",[14,1215,1216,1217,1219,1220,30],{},"Font CLS is a metrics problem, not a loading problem. Self-host and preload so the swap happens early, then declare a metric-matched fallback so the swap changes glyph shapes and nothing else. Verify with a throttled run rather than a warm local load, and if the typeface is not doing brand work, ",[44,1218,1043],{}," gets you the same zero for a fraction of the effort. Then check what is left with the field-data methods in ",[21,1221,1223],{"href":1222},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js\u002F","Measuring CLS in the Field With web-vitals.js",[32,1225,1227],{"id":1226},"faq","FAQ",[211,1229,1231],{"id":1230},"should-i-use-font-display-swap-or-optional","Should I use font-display swap or optional?",[14,1233,1234,1235,1237,1238,1240],{},"Use ",[44,1236,451],{}," with metric-matched fallback overrides when the typeface matters to the brand, because the reader always gets the real font eventually. Use ",[44,1239,836],{}," when it does not: the browser gives the font a 100 millisecond window and otherwise keeps the fallback for that page view, which guarantees zero shift at the cost of an occasional unstyled page.",[211,1242,1244],{"id":1243},"how-do-i-find-the-right-size-adjust-value","How do I find the right size-adjust value?",[14,1246,1247],{},"Divide the fallback font's average character width by the web font's, or measure empirically by rendering a long paragraph in both faces at the same size and comparing the rendered block height. Tools such as the Fontaine plugin and Malte Ubl's fallback generator compute all four override values from the font file directly.",[211,1249,1251],{"id":1250},"does-preloading-the-font-remove-the-shift","Does preloading the font remove the shift?",[14,1253,1254],{},"It shortens the window in which the fallback is visible but does not eliminate the shift, because the swap still happens. Preloading plus metric overrides is the combination that gets you to zero: the overrides make the swap invisible and the preload makes it early.",[211,1256,1258],{"id":1257},"do-variable-fonts-change-any-of-this","Do variable fonts change any of this?",[14,1260,1261],{},"No, the mechanism is identical — a variable font is still one file that swaps in. It usually helps overall because one variable file replaces four or five static weights, so there are fewer requests racing and fewer faces whose metrics need matching.",[211,1263,1265],{"id":1264},"why-does-my-heading-still-jump-when-the-body-text-does-not","Why does my heading still jump when the body text does not?",[14,1267,1268],{},"Headings are usually set in a different face or weight from body text, and each face needs its own metric-matched fallback. A single override tuned for the body font will be wrong for a display face at 2.5 rem, where a small percentage error becomes several pixels of line height.",[32,1270,1272],{"id":1271},"related","Related",[37,1274,1275,1283,1288,1293,1300],{},[40,1276,1277,1184,1280,1282],{},[503,1278,1279],{},"Parent:",[21,1281,24],{"href":23}," — the other three causes of shift.",[40,1284,1285,1287],{},[21,1286,51],{"href":50}," — the delivery half of this fix.",[40,1289,1290,1292],{},[21,1291,827],{"href":826}," — get the file under 40 KB.",[40,1294,1295,1299],{},[21,1296,1298],{"href":1297},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift\u002F","Reserving Space for Images and Embeds"," — fix this first; it is usually the bigger number.",[40,1301,1302,1304],{},[21,1303,29],{"href":28}," — the wider loading policy this fits into.",[1306,1307,1308],"style",{},"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 .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}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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":227,"searchDepth":243,"depth":243,"links":1310},[1311,1312,1313,1320,1321,1322,1323,1330],{"id":34,"depth":243,"text":35},{"id":60,"depth":243,"text":61},{"id":208,"depth":243,"text":209,"children":1314},[1315,1316,1317,1318,1319],{"id":213,"depth":260,"text":214},{"id":509,"depth":260,"text":510},{"id":752,"depth":260,"text":753},{"id":819,"depth":260,"text":820},{"id":830,"depth":260,"text":831},{"id":946,"depth":243,"text":947},{"id":1154,"depth":243,"text":1155},{"id":1212,"depth":243,"text":1213},{"id":1226,"depth":243,"text":1227,"children":1324},[1325,1326,1327,1328,1329],{"id":1230,"depth":260,"text":1231},{"id":1243,"depth":260,"text":1244},{"id":1250,"depth":260,"text":1251},{"id":1257,"depth":260,"text":1258},{"id":1264,"depth":260,"text":1265},{"id":1271,"depth":243,"text":1272},[1332,1334,1337,1338],{"name":1333,"item":632},"Home",{"name":1335,"item":1336},"Performance Optimization & Core Web Vitals for SSGs","\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002F",{"name":24,"item":23},{"name":5,"item":1339},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts\u002F","2026-08-01","Kill font-swap CLS with size-adjust, ascent-override and a preloaded subset. Includes how to compute the override values and the measured 0.14 to 0.00 result.","md",[1344,1348,1349,1353,1354],{"q":1231,"a":1345},{"Use swap with metric-matched fallback overrides when the typeface matters to the brand, because the reader always gets the real font eventually":1346},{" Use optional when it does not":1347},"the browser gives the font a 100 millisecond window and otherwise keeps the fallback for that page view, which guarantees zero shift at the cost of an occasional unstyled page.",{"q":1244,"a":1247},{"q":1251,"a":1350},{"It shortens the window in which the fallback is visible but does not eliminate the shift, because the swap still happens":1351},{" Preloading plus metric overrides is the combination that gets you to zero":1352},"the overrides make the swap invisible and the preload makes it early.",{"q":1258,"a":1261},{"q":1265,"a":1268},{},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts",{"title":5,"description":1341},"performance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts\u002Findex","article","4wMnchwRIOvckGD21tg8lHnTE440AgnsjeQ5xGjsYq4",[1362,1365,1368,1371,1374,1377,1380,1383,1386,1389,1392,1395,1398,1401,1404,1407,1410,1413,1416,1419,1422,1425,1428,1431,1434,1437,1440,1443,1446,1449,1452,1453,1456,1458,1460,1462,1464,1467,1469,1472,1475,1478,1481,1484,1487,1490,1493,1496,1499,1502,1505,1508,1511,1514,1517,1520,1523,1526,1529,1532,1535,1538,1541,1544,1547,1550,1553,1556,1559,1562,1565,1568,1571,1574,1577,1580,1583,1586,1589,1592,1595,1598,1601],{"path":1363,"title":1364},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1366,"title":1367},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1369,"title":1370},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1372,"title":1373},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1375,"title":1376},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1378,"title":1379},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1381,"title":1382},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1384,"title":1385},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1387,"title":1388},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1390,"title":1391},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1393,"title":1394},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1396,"title":1397},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1399,"title":1400},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1402,"title":1403},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1405,"title":1406},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1408,"title":1409},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1411,"title":1412},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1414,"title":1415},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1417,"title":1418},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1420,"title":1421},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1423,"title":1424},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1426,"title":1427},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1429,"title":1430},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1432,"title":1433},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1435,"title":1436},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1438,"title":1439},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1441,"title":1442},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1444,"title":1445},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1447,"title":1448},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1450,"title":1451},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1356,"title":5},{"path":1454,"title":1455},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1457,"title":24},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites",{"path":1459,"title":1223},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js",{"path":1461,"title":1298},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift",{"path":1463,"title":29},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites",{"path":1465,"title":1466},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1468,"title":827},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render",{"path":1470,"title":1471},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1473,"title":1474},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1476,"title":1477},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1479,"title":1480},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1482,"title":1483},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1485,"title":1486},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1488,"title":1489},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1491,"title":1492},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1494,"title":1495},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1497,"title":1498},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1500,"title":1501},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1503,"title":1504},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1506,"title":1507},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":1509,"title":1510},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":1512,"title":1513},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1515,"title":1516},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":1518,"title":1519},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":1521,"title":1522},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1524,"title":1525},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":1527,"title":1528},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1530,"title":1531},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":1533,"title":1534},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1536,"title":1537},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":1539,"title":1540},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":1542,"title":1543},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":1545,"title":1546},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":1548,"title":1549},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":1551,"title":1552},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1554,"title":1555},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":1557,"title":1558},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1560,"title":1561},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1563,"title":1564},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":1566,"title":1567},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":1569,"title":1570},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1572,"title":1573},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1575,"title":1576},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1578,"title":1579},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":1581,"title":1582},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":1584,"title":1585},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1587,"title":1588},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1590,"title":1591},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1593,"title":1594},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1596,"title":1597},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1599,"title":1600},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":1602,"title":1603},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",1785611671075]