[{"data":1,"prerenderedAt":2745},["ShallowReactive",2],{"page-\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions":3,"content-all-pages":2180,"live-demo:theme-switch-crossfade":2739},{"id":4,"title":5,"body":6,"description":2162,"extension":2163,"meta":2164,"navigation":571,"path":2176,"seo":2177,"stem":2178,"__hash__":2179},"content\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions\u002Findex.md","Smooth Theme Switching: Crossfades, Circular Reveals and No Flash",{"type":7,"value":8,"toc":2150},"minimark",[9,13,33,38,46,49,156,160,163,1697,1704,1708,1719,1781,1803,1807,1810,1831,1837,1855,1861,1865,1884,1891,1897,2044,2051,2055,2058,2062,2082,2086,2092,2098,2107,2113,2117,2147],[10,11,5],"h1",{"id":12},"smooth-theme-switching-crossfades-circular-reveals-and-no-flash",[14,15,16,17,21,22,27,28,32],"p",{},"A theme toggle is one of the few moments where the entire page changes at once. Done abruptly, every surface flips in a single frame, which is jarring in a dark room and can read as a glitch. Done badly in the other direction — a ",[18,19,20],"code",{},"* { transition: all 0.3s }"," rule — every element animates independently on slightly different schedules, hovers start lagging all over the site, and low-end devices stutter through the switch. The narrow problem here is making the switch feel deliberate: either a scoped crossfade of the main surfaces, or a single composited view transition such as a circular reveal from the toggle, without a flash of the wrong theme on load. It belongs to ",[23,24,26],"a",{"href":25},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002F","Color & Theme Transitions"," in the ",[23,29,31],{"href":30},"\u002Fcss-only-micro-interactions-animations\u002F","CSS-Only Micro-Interactions & Animations"," guide.",[34,35,37],"h2",{"id":36},"why-a-theme-switch-is-a-special-animation","Why a theme switch is a special animation",[14,39,40,41,45],{},"Most transitions involve one component. A theme switch involves every themed property of every element on the page changing in the same frame: backgrounds, text colours, borders, shadows, SVG fills. Animating all of those individually means potentially thousands of concurrent transitions, each needing style recalculation and repaint per frame. It also means every ",[42,43,44],"em",{},"other"," colour change on the site — hovers, focus rings, validation states — gains the same transition, because the rule cannot tell a theme switch from any other change.",[14,47,48],{},"There are two ways around that. The first limits the crossfade to the handful of large surfaces that dominate what the eye sees, and lets small details change instantly. The second avoids per-element animation entirely: a view transition snapshots the page before and after the change and animates between two images on the compositor. Both are covered here.",[50,51,57,58,57,62,57,66,57,74,57,86,57,92,57,97,57,101,57,105,57,110,57,115,57,119,57,122,57,125,57,128,57,131,57,135,57,139,57,142,57,145,57,148,57,151],"svg",{"viewBox":52,"role":53,"ariaLabel":54,"xmlns":55,"style":56},"0 0 720 300","img","Comparison of per-element colour transitions, scoped surface transitions, and a snapshot-based view transition","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[59,60,61],"title",{},"Three ways to animate a theme switch",[63,64,65],"desc",{},"A universal transition animates thousands of elements and slows hovers everywhere. A scoped transition animates a few large surfaces. A view transition animates two snapshots of the page, old and new, as images.",[67,68,73],"text",{"textAnchor":69,"x":70,"y":71,"style":72},"middle","360","26","text-anchor:middle;fill:currentColor;font:700 17px sans-serif","How much work each approach asks of the browser",[75,76],"rect",{"x":77,"y":78,"width":79,"height":80,"rx":81,"fill":82,"stroke":83,"strokeWidth":84,"opacity":85},"20","50","220","190","10","none","currentColor","1.5","0.6",[67,87,91],{"textAnchor":69,"x":88,"y":89,"style":90},"130","76","text-anchor:middle;fill:currentColor;font:700 13px ui-monospace,monospace","* { transition }",[67,93,96],{"textAnchor":69,"x":88,"y":94,"style":95},"110","text-anchor:middle;fill:currentColor;font:12px sans-serif","every element animates",[67,98,100],{"textAnchor":69,"x":88,"y":99,"style":95},"132","hovers lag site-wide",[67,102,104],{"textAnchor":69,"x":88,"y":103,"style":95},"154","frames drop on switch",[67,106,109],{"textAnchor":69,"x":88,"y":107,"style":108},"216","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","avoid",[75,111],{"x":112,"y":78,"width":79,"height":80,"rx":81,"fill":113,"opacity":114},"250","#7aa2ff","0.18",[67,116,118],{"textAnchor":69,"x":70,"y":89,"style":117},"text-anchor:middle;fill:currentColor;font:700 13px sans-serif","Scoped surfaces",[67,120,121],{"textAnchor":69,"x":70,"y":94,"style":95},"body, header, cards",[67,123,124],{"textAnchor":69,"x":70,"y":99,"style":95},"details switch instantly",[67,126,127],{"textAnchor":69,"x":70,"y":103,"style":95},"works everywhere",[67,129,130],{"textAnchor":69,"x":70,"y":107,"style":108},"good baseline",[75,132],{"x":133,"y":78,"width":79,"height":80,"rx":81,"fill":113,"opacity":134},"480","0.34",[67,136,138],{"textAnchor":69,"x":137,"y":89,"style":117},"590","View transition",[67,140,141],{"textAnchor":69,"x":137,"y":94,"style":95},"two snapshots animate",[67,143,144],{"textAnchor":69,"x":137,"y":99,"style":95},"on the compositor",[67,146,147],{"textAnchor":69,"x":137,"y":103,"style":95},"enables reveals",[67,149,150],{"textAnchor":69,"x":137,"y":107,"style":108},"best effect",[67,152,155],{"textAnchor":69,"x":70,"y":153,"style":154},"276","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.8","Use the view transition where supported and the scoped crossfade as its fallback.",[34,157,159],{"id":158},"the-complete-implementation","The complete implementation",[14,161,162],{},"The page below combines all three pieces: a no-flash initial theme, a scoped crossfade fallback, and a circular view-transition reveal that expands from the toggle button. The script is minimal and only orchestrates; every visual is CSS.",[164,165,170],"pre",{"className":166,"code":167,"language":168,"meta":169,"style":169},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Ctitle>Theme switch\u003C\u002Ftitle>\n\u003Cscript>\n  \u002F\u002F 1. No flash: apply the stored or system theme before first paint.\n  try {\n    const stored = localStorage.getItem('theme');\n    const dark = stored ? stored === 'dark' : matchMedia('(prefers-color-scheme: dark)').matches;\n    document.documentElement.dataset.theme = dark ? 'dark' : 'light';\n  } catch (e) {}\n\u003C\u002Fscript>\n\u003Cstyle>\n  :root {\n    --surface: #ffffff;\n    --text: #0f172a;\n    --card: #f1f5f9;\n    color-scheme: light;\n  }\n  :root[data-theme=\"dark\"] {\n    --surface: #0f172a;\n    --text: #e2e8f0;\n    --card: #1e293b;\n    color-scheme: dark;\n  }\n\n  body { margin: 0; padding: 1.5rem; font: 16px\u002F1.6 system-ui, sans-serif; background: var(--surface); color: var(--text); }\n  .card { padding: 1rem; border-radius: 12px; background: var(--card); }\n\n  \u002F* 2. Fallback crossfade, scoped to the big surfaces only. The class is\n     added just for the duration of a switch, so hovers are unaffected. *\u002F\n  :root.theme-fading body,\n  :root.theme-fading .card {\n    transition: background-color 250ms ease, color 250ms ease;\n  }\n\n  \u002F* 3. Circular reveal via view transition. The new snapshot is clipped\n     to a circle that grows from the toggle's position. *\u002F\n  ::view-transition-old(root),\n  ::view-transition-new(root) {\n    animation: none;\n    mix-blend-mode: normal;\n  }\n  ::view-transition-new(root) {\n    animation: theme-reveal 450ms cubic-bezier(0.22, 1, 0.36, 1);\n  }\n  @keyframes theme-reveal {\n    from { clip-path: circle(0 at var(--x, 50%) var(--y, 50%)); }\n    to   { clip-path: circle(150% at var(--x, 50%) var(--y, 50%)); }\n  }\n\n  \u002F* Reduced motion: no sweeping reveal, just a quick crossfade. *\u002F\n  @media (prefers-reduced-motion: reduce) {\n    ::view-transition-new(root) { animation: fade-in 150ms ease; }\n    @keyframes fade-in { from { opacity: 0; } }\n  }\n\n  .toggle:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cbutton class=\"toggle\" type=\"button\" aria-pressed=\"false\">Dark theme\u003C\u002Fbutton>\n  \u003Cdiv class=\"card\">\u003Cp>Content that changes with the theme.\u003C\u002Fp>\u003C\u002Fdiv>\n\n  \u003Cscript>\n    const btn = document.querySelector('.toggle');\n    const root = document.documentElement;\n    btn.setAttribute('aria-pressed', String(root.dataset.theme === 'dark'));\n\n    btn.addEventListener('click', (event) => {\n      const next = root.dataset.theme === 'dark' ? 'light' : 'dark';\n      const apply = () => {\n        root.dataset.theme = next;\n        btn.setAttribute('aria-pressed', String(next === 'dark'));\n        try { localStorage.setItem('theme', next); } catch (e) {}\n      };\n\n      \u002F\u002F Centre the reveal on the button.\n      const r = btn.getBoundingClientRect();\n      root.style.setProperty('--x', `${r.left + r.width \u002F 2}px`);\n      root.style.setProperty('--y', `${r.top + r.height \u002F 2}px`);\n\n      if (document.startViewTransition) {\n        document.startViewTransition(apply);\n      } else {\n        root.classList.add('theme-fading');\n        apply();\n        setTimeout(() => root.classList.remove('theme-fading'), 300);\n      }\n    });\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n","html","",[18,171,172,192,212,222,240,265,279,289,296,306,334,372,395,407,417,427,435,450,463,476,489,495,514,525,537,549,561,566,573,664,708,713,719,725,737,747,777,782,787,793,799,805,811,823,836,841,846,882,887,898,955,1007,1012,1017,1023,1032,1053,1079,1084,1089,1127,1136,1145,1155,1195,1227,1232,1241,1264,1277,1306,1311,1337,1366,1383,1394,1419,1442,1448,1453,1459,1478,1527,1567,1572,1581,1593,1604,1620,1628,1657,1663,1669,1679,1688],{"__ignoreMap":169},[173,174,177,181,185,189],"span",{"class":175,"line":176},"line",1,[173,178,180],{"class":179},"suds8","\u003C!",[173,182,184],{"class":183},"sne4z","doctype",[173,186,188],{"class":187},"s-5SL"," html",[173,190,191],{"class":179},">\n",[173,193,195,198,200,203,206,210],{"class":175,"line":194},2,[173,196,197],{"class":179},"\u003C",[173,199,168],{"class":183},[173,201,202],{"class":187}," lang",[173,204,205],{"class":179},"=",[173,207,209],{"class":208},"sT6z2","\"en\"",[173,211,191],{"class":179},[173,213,215,217,220],{"class":175,"line":214},3,[173,216,197],{"class":179},[173,218,219],{"class":183},"head",[173,221,191],{"class":179},[173,223,225,227,230,233,235,238],{"class":175,"line":224},4,[173,226,197],{"class":179},[173,228,229],{"class":183},"meta",[173,231,232],{"class":187}," charset",[173,234,205],{"class":179},[173,236,237],{"class":208},"\"utf-8\"",[173,239,191],{"class":179},[173,241,243,245,247,250,252,255,258,260,263],{"class":175,"line":242},5,[173,244,197],{"class":179},[173,246,229],{"class":183},[173,248,249],{"class":187}," name",[173,251,205],{"class":179},[173,253,254],{"class":208},"\"viewport\"",[173,256,257],{"class":187}," content",[173,259,205],{"class":179},[173,261,262],{"class":208},"\"width=device-width, initial-scale=1\"",[173,264,191],{"class":179},[173,266,268,270,272,275,277],{"class":175,"line":267},6,[173,269,197],{"class":179},[173,271,59],{"class":183},[173,273,274],{"class":179},">Theme switch\u003C\u002F",[173,276,59],{"class":183},[173,278,191],{"class":179},[173,280,282,284,287],{"class":175,"line":281},7,[173,283,197],{"class":179},[173,285,286],{"class":183},"script",[173,288,191],{"class":179},[173,290,292],{"class":175,"line":291},8,[173,293,295],{"class":294},"sLBg1","  \u002F\u002F 1. No flash: apply the stored or system theme before first paint.\n",[173,297,299,303],{"class":175,"line":298},9,[173,300,302],{"class":301},"sHUrx","  try",[173,304,305],{"class":179}," {\n",[173,307,309,312,315,318,321,325,328,331],{"class":175,"line":308},10,[173,310,311],{"class":301},"    const",[173,313,314],{"class":187}," stored",[173,316,317],{"class":301}," =",[173,319,320],{"class":179}," localStorage.",[173,322,324],{"class":323},"sKwhi","getItem",[173,326,327],{"class":179},"(",[173,329,330],{"class":208},"'theme'",[173,332,333],{"class":179},");\n",[173,335,337,339,342,344,347,350,352,355,358,361,364,366,369],{"class":175,"line":336},11,[173,338,311],{"class":301},[173,340,341],{"class":187}," dark",[173,343,317],{"class":301},[173,345,346],{"class":179}," stored ",[173,348,349],{"class":301},"?",[173,351,346],{"class":179},[173,353,354],{"class":301},"===",[173,356,357],{"class":208}," 'dark'",[173,359,360],{"class":301}," :",[173,362,363],{"class":323}," matchMedia",[173,365,327],{"class":179},[173,367,368],{"class":208},"'(prefers-color-scheme: dark)'",[173,370,371],{"class":179},").matches;\n",[173,373,375,378,380,383,385,387,389,392],{"class":175,"line":374},12,[173,376,377],{"class":179},"    document.documentElement.dataset.theme ",[173,379,205],{"class":301},[173,381,382],{"class":179}," dark ",[173,384,349],{"class":301},[173,386,357],{"class":208},[173,388,360],{"class":301},[173,390,391],{"class":208}," 'light'",[173,393,394],{"class":179},";\n",[173,396,398,401,404],{"class":175,"line":397},13,[173,399,400],{"class":179},"  } ",[173,402,403],{"class":301},"catch",[173,405,406],{"class":179}," (e) {}\n",[173,408,410,413,415],{"class":175,"line":409},14,[173,411,412],{"class":179},"\u003C\u002F",[173,414,286],{"class":183},[173,416,191],{"class":179},[173,418,420,422,425],{"class":175,"line":419},15,[173,421,197],{"class":179},[173,423,424],{"class":183},"style",[173,426,191],{"class":179},[173,428,430,433],{"class":175,"line":429},16,[173,431,432],{"class":187},"  :root",[173,434,305],{"class":179},[173,436,438,442,445,448],{"class":175,"line":437},17,[173,439,441],{"class":440},"soyes","    --surface",[173,443,444],{"class":179},": ",[173,446,447],{"class":187},"#ffffff",[173,449,394],{"class":179},[173,451,453,456,458,461],{"class":175,"line":452},18,[173,454,455],{"class":440},"    --text",[173,457,444],{"class":179},[173,459,460],{"class":187},"#0f172a",[173,462,394],{"class":179},[173,464,466,469,471,474],{"class":175,"line":465},19,[173,467,468],{"class":440},"    --card",[173,470,444],{"class":179},[173,472,473],{"class":187},"#f1f5f9",[173,475,394],{"class":179},[173,477,479,482,484,487],{"class":175,"line":478},20,[173,480,481],{"class":187},"    color-scheme",[173,483,444],{"class":179},[173,485,486],{"class":187},"light",[173,488,394],{"class":179},[173,490,492],{"class":175,"line":491},21,[173,493,494],{"class":179},"  }\n",[173,496,498,500,503,506,508,511],{"class":175,"line":497},22,[173,499,432],{"class":187},[173,501,502],{"class":179},"[",[173,504,505],{"class":187},"data-theme",[173,507,205],{"class":301},[173,509,510],{"class":208},"\"dark\"",[173,512,513],{"class":179},"] {\n",[173,515,517,519,521,523],{"class":175,"line":516},23,[173,518,441],{"class":440},[173,520,444],{"class":179},[173,522,460],{"class":187},[173,524,394],{"class":179},[173,526,528,530,532,535],{"class":175,"line":527},24,[173,529,455],{"class":440},[173,531,444],{"class":179},[173,533,534],{"class":187},"#e2e8f0",[173,536,394],{"class":179},[173,538,540,542,544,547],{"class":175,"line":539},25,[173,541,468],{"class":440},[173,543,444],{"class":179},[173,545,546],{"class":187},"#1e293b",[173,548,394],{"class":179},[173,550,552,554,556,559],{"class":175,"line":551},26,[173,553,481],{"class":187},[173,555,444],{"class":179},[173,557,558],{"class":187},"dark",[173,560,394],{"class":179},[173,562,564],{"class":175,"line":563},27,[173,565,494],{"class":179},[173,567,569],{"class":175,"line":568},28,[173,570,572],{"emptyLinePlaceholder":571},true,"\n",[173,574,576,579,582,585,587,590,593,596,598,600,603,605,608,610,613,616,619,622,625,628,631,633,636,638,641,643,646,649,652,654,656,658,661],{"class":175,"line":575},29,[173,577,578],{"class":183},"  body",[173,580,581],{"class":179}," { ",[173,583,584],{"class":187},"margin",[173,586,444],{"class":179},[173,588,589],{"class":187},"0",[173,591,592],{"class":179},"; ",[173,594,595],{"class":187},"padding",[173,597,444],{"class":179},[173,599,84],{"class":187},[173,601,602],{"class":301},"rem",[173,604,592],{"class":179},[173,606,607],{"class":187},"font",[173,609,444],{"class":179},[173,611,612],{"class":187},"16",[173,614,615],{"class":301},"px",[173,617,618],{"class":179},"\u002F",[173,620,621],{"class":187},"1.6",[173,623,624],{"class":187}," system-ui",[173,626,627],{"class":179},", ",[173,629,630],{"class":187},"sans-serif",[173,632,592],{"class":179},[173,634,635],{"class":187},"background",[173,637,444],{"class":179},[173,639,640],{"class":187},"var",[173,642,327],{"class":179},[173,644,645],{"class":440},"--surface",[173,647,648],{"class":179},"); ",[173,650,651],{"class":187},"color",[173,653,444],{"class":179},[173,655,640],{"class":187},[173,657,327],{"class":179},[173,659,660],{"class":440},"--text",[173,662,663],{"class":179},"); }\n",[173,665,667,670,672,674,676,679,681,683,686,688,691,693,695,697,699,701,703,706],{"class":175,"line":666},30,[173,668,669],{"class":187},"  .card",[173,671,581],{"class":179},[173,673,595],{"class":187},[173,675,444],{"class":179},[173,677,678],{"class":187},"1",[173,680,602],{"class":301},[173,682,592],{"class":179},[173,684,685],{"class":187},"border-radius",[173,687,444],{"class":179},[173,689,690],{"class":187},"12",[173,692,615],{"class":301},[173,694,592],{"class":179},[173,696,635],{"class":187},[173,698,444],{"class":179},[173,700,640],{"class":187},[173,702,327],{"class":179},[173,704,705],{"class":440},"--card",[173,707,663],{"class":179},[173,709,711],{"class":175,"line":710},31,[173,712,572],{"emptyLinePlaceholder":571},[173,714,716],{"class":175,"line":715},32,[173,717,718],{"class":294},"  \u002F* 2. Fallback crossfade, scoped to the big surfaces only. The class is\n",[173,720,722],{"class":175,"line":721},33,[173,723,724],{"class":294},"     added just for the duration of a switch, so hovers are unaffected. *\u002F\n",[173,726,728,731,734],{"class":175,"line":727},34,[173,729,730],{"class":187},"  :root.theme-fading",[173,732,733],{"class":183}," body",[173,735,736],{"class":179},",\n",[173,738,740,742,745],{"class":175,"line":739},35,[173,741,730],{"class":187},[173,743,744],{"class":187}," .card",[173,746,305],{"class":179},[173,748,750,753,756,758,761,764,766,768,771,773,775],{"class":175,"line":749},36,[173,751,752],{"class":187},"    transition",[173,754,755],{"class":179},": background-color ",[173,757,112],{"class":187},[173,759,760],{"class":301},"ms",[173,762,763],{"class":187}," ease",[173,765,627],{"class":179},[173,767,651],{"class":187},[173,769,770],{"class":187}," 250",[173,772,760],{"class":301},[173,774,763],{"class":187},[173,776,394],{"class":179},[173,778,780],{"class":175,"line":779},37,[173,781,494],{"class":179},[173,783,785],{"class":175,"line":784},38,[173,786,572],{"emptyLinePlaceholder":571},[173,788,790],{"class":175,"line":789},39,[173,791,792],{"class":294},"  \u002F* 3. Circular reveal via view transition. The new snapshot is clipped\n",[173,794,796],{"class":175,"line":795},40,[173,797,798],{"class":294},"     to a circle that grows from the toggle's position. *\u002F\n",[173,800,802],{"class":175,"line":801},41,[173,803,804],{"class":179},"  ::view-transition-old(root),\n",[173,806,808],{"class":175,"line":807},42,[173,809,810],{"class":179},"  ::view-transition-new(root) {\n",[173,812,814,817,819,821],{"class":175,"line":813},43,[173,815,816],{"class":187},"    animation",[173,818,444],{"class":179},[173,820,82],{"class":187},[173,822,394],{"class":179},[173,824,826,829,831,834],{"class":175,"line":825},44,[173,827,828],{"class":187},"    mix-blend-mode",[173,830,444],{"class":179},[173,832,833],{"class":187},"normal",[173,835,394],{"class":179},[173,837,839],{"class":175,"line":838},45,[173,840,494],{"class":179},[173,842,844],{"class":175,"line":843},46,[173,845,810],{"class":179},[173,847,849,851,854,857,859,862,864,867,869,871,873,876,878,880],{"class":175,"line":848},47,[173,850,816],{"class":187},[173,852,853],{"class":179},": theme-reveal ",[173,855,856],{"class":187},"450",[173,858,760],{"class":301},[173,860,861],{"class":187}," cubic-bezier",[173,863,327],{"class":179},[173,865,866],{"class":187},"0.22",[173,868,627],{"class":179},[173,870,678],{"class":187},[173,872,627],{"class":179},[173,874,875],{"class":187},"0.36",[173,877,627],{"class":179},[173,879,678],{"class":187},[173,881,333],{"class":179},[173,883,885],{"class":175,"line":884},48,[173,886,494],{"class":179},[173,888,890,893,896],{"class":175,"line":889},49,[173,891,892],{"class":301},"  @keyframes",[173,894,895],{"class":440}," theme-reveal",[173,897,305],{"class":179},[173,899,901,904,906,909,911,914,916,918,921,924,926,929,931,933,936,939,941,943,946,948,950,952],{"class":175,"line":900},50,[173,902,903],{"class":187},"    from",[173,905,581],{"class":179},[173,907,908],{"class":187},"clip-path",[173,910,444],{"class":179},[173,912,913],{"class":187},"circle",[173,915,327],{"class":179},[173,917,589],{"class":187},[173,919,920],{"class":301}," at",[173,922,923],{"class":187}," var",[173,925,327],{"class":179},[173,927,928],{"class":440},"--x",[173,930,627],{"class":179},[173,932,78],{"class":187},[173,934,935],{"class":301},"%",[173,937,938],{"class":179},") ",[173,940,640],{"class":187},[173,942,327],{"class":179},[173,944,945],{"class":440},"--y",[173,947,627],{"class":179},[173,949,78],{"class":187},[173,951,935],{"class":301},[173,953,954],{"class":179},")); }\n",[173,956,958,961,964,966,968,970,972,975,977,979,981,983,985,987,989,991,993,995,997,999,1001,1003,1005],{"class":175,"line":957},51,[173,959,960],{"class":187},"    to",[173,962,963],{"class":179},"   { ",[173,965,908],{"class":187},[173,967,444],{"class":179},[173,969,913],{"class":187},[173,971,327],{"class":179},[173,973,974],{"class":187},"150",[173,976,935],{"class":301},[173,978,920],{"class":301},[173,980,923],{"class":187},[173,982,327],{"class":179},[173,984,928],{"class":440},[173,986,627],{"class":179},[173,988,78],{"class":187},[173,990,935],{"class":301},[173,992,938],{"class":179},[173,994,640],{"class":187},[173,996,327],{"class":179},[173,998,945],{"class":440},[173,1000,627],{"class":179},[173,1002,78],{"class":187},[173,1004,935],{"class":301},[173,1006,954],{"class":179},[173,1008,1010],{"class":175,"line":1009},52,[173,1011,494],{"class":179},[173,1013,1015],{"class":175,"line":1014},53,[173,1016,572],{"emptyLinePlaceholder":571},[173,1018,1020],{"class":175,"line":1019},54,[173,1021,1022],{"class":294},"  \u002F* Reduced motion: no sweeping reveal, just a quick crossfade. *\u002F\n",[173,1024,1026,1029],{"class":175,"line":1025},55,[173,1027,1028],{"class":301},"  @media",[173,1030,1031],{"class":179}," (prefers-reduced-motion: reduce) {\n",[173,1033,1035,1038,1041,1044,1046,1048,1050],{"class":175,"line":1034},56,[173,1036,1037],{"class":179},"    ::view-transition-new(root) { ",[173,1039,1040],{"class":187},"animation",[173,1042,1043],{"class":179},": fade-in ",[173,1045,974],{"class":187},[173,1047,760],{"class":301},[173,1049,763],{"class":187},[173,1051,1052],{"class":179},"; }\n",[173,1054,1056,1059,1062,1064,1067,1069,1072,1074,1076],{"class":175,"line":1055},57,[173,1057,1058],{"class":301},"    @keyframes",[173,1060,1061],{"class":440}," fade-in",[173,1063,581],{"class":179},[173,1065,1066],{"class":187},"from",[173,1068,581],{"class":179},[173,1070,1071],{"class":187},"opacity",[173,1073,444],{"class":179},[173,1075,589],{"class":187},[173,1077,1078],{"class":179},"; } }\n",[173,1080,1082],{"class":175,"line":1081},58,[173,1083,494],{"class":179},[173,1085,1087],{"class":175,"line":1086},59,[173,1088,572],{"emptyLinePlaceholder":571},[173,1090,1092,1095,1097,1100,1102,1105,1107,1110,1113,1115,1118,1120,1123,1125],{"class":175,"line":1091},60,[173,1093,1094],{"class":187},"  .toggle:focus-visible",[173,1096,581],{"class":179},[173,1098,1099],{"class":187},"outline",[173,1101,444],{"class":179},[173,1103,1104],{"class":187},"2",[173,1106,615],{"class":301},[173,1108,1109],{"class":187}," solid",[173,1111,1112],{"class":187}," currentColor",[173,1114,592],{"class":179},[173,1116,1117],{"class":187},"outline-offset",[173,1119,444],{"class":179},[173,1121,1122],{"class":187},"3",[173,1124,615],{"class":301},[173,1126,1052],{"class":179},[173,1128,1130,1132,1134],{"class":175,"line":1129},61,[173,1131,412],{"class":179},[173,1133,424],{"class":183},[173,1135,191],{"class":179},[173,1137,1139,1141,1143],{"class":175,"line":1138},62,[173,1140,412],{"class":179},[173,1142,219],{"class":183},[173,1144,191],{"class":179},[173,1146,1148,1150,1153],{"class":175,"line":1147},63,[173,1149,197],{"class":179},[173,1151,1152],{"class":183},"body",[173,1154,191],{"class":179},[173,1156,1158,1161,1164,1167,1169,1172,1175,1177,1180,1183,1185,1188,1191,1193],{"class":175,"line":1157},64,[173,1159,1160],{"class":179},"  \u003C",[173,1162,1163],{"class":183},"button",[173,1165,1166],{"class":187}," class",[173,1168,205],{"class":179},[173,1170,1171],{"class":208},"\"toggle\"",[173,1173,1174],{"class":187}," type",[173,1176,205],{"class":179},[173,1178,1179],{"class":208},"\"button\"",[173,1181,1182],{"class":187}," aria-pressed",[173,1184,205],{"class":179},[173,1186,1187],{"class":208},"\"false\"",[173,1189,1190],{"class":179},">Dark theme\u003C\u002F",[173,1192,1163],{"class":183},[173,1194,191],{"class":179},[173,1196,1198,1200,1203,1205,1207,1210,1213,1215,1218,1220,1223,1225],{"class":175,"line":1197},65,[173,1199,1160],{"class":179},[173,1201,1202],{"class":183},"div",[173,1204,1166],{"class":187},[173,1206,205],{"class":179},[173,1208,1209],{"class":208},"\"card\"",[173,1211,1212],{"class":179},">\u003C",[173,1214,14],{"class":183},[173,1216,1217],{"class":179},">Content that changes with the theme.\u003C\u002F",[173,1219,14],{"class":183},[173,1221,1222],{"class":179},">\u003C\u002F",[173,1224,1202],{"class":183},[173,1226,191],{"class":179},[173,1228,1230],{"class":175,"line":1229},66,[173,1231,572],{"emptyLinePlaceholder":571},[173,1233,1235,1237,1239],{"class":175,"line":1234},67,[173,1236,1160],{"class":179},[173,1238,286],{"class":183},[173,1240,191],{"class":179},[173,1242,1244,1246,1249,1251,1254,1257,1259,1262],{"class":175,"line":1243},68,[173,1245,311],{"class":301},[173,1247,1248],{"class":187}," btn",[173,1250,317],{"class":301},[173,1252,1253],{"class":179}," document.",[173,1255,1256],{"class":323},"querySelector",[173,1258,327],{"class":179},[173,1260,1261],{"class":208},"'.toggle'",[173,1263,333],{"class":179},[173,1265,1267,1269,1272,1274],{"class":175,"line":1266},69,[173,1268,311],{"class":301},[173,1270,1271],{"class":187}," root",[173,1273,317],{"class":301},[173,1275,1276],{"class":179}," document.documentElement;\n",[173,1278,1280,1283,1286,1288,1291,1293,1296,1299,1301,1303],{"class":175,"line":1279},70,[173,1281,1282],{"class":179},"    btn.",[173,1284,1285],{"class":323},"setAttribute",[173,1287,327],{"class":179},[173,1289,1290],{"class":208},"'aria-pressed'",[173,1292,627],{"class":179},[173,1294,1295],{"class":323},"String",[173,1297,1298],{"class":179},"(root.dataset.theme ",[173,1300,354],{"class":301},[173,1302,357],{"class":208},[173,1304,1305],{"class":179},"));\n",[173,1307,1309],{"class":175,"line":1308},71,[173,1310,572],{"emptyLinePlaceholder":571},[173,1312,1314,1316,1319,1321,1324,1327,1330,1332,1335],{"class":175,"line":1313},72,[173,1315,1282],{"class":179},[173,1317,1318],{"class":323},"addEventListener",[173,1320,327],{"class":179},[173,1322,1323],{"class":208},"'click'",[173,1325,1326],{"class":179},", (",[173,1328,1329],{"class":440},"event",[173,1331,938],{"class":179},[173,1333,1334],{"class":301},"=>",[173,1336,305],{"class":179},[173,1338,1340,1343,1346,1348,1351,1353,1355,1358,1360,1362,1364],{"class":175,"line":1339},73,[173,1341,1342],{"class":301},"      const",[173,1344,1345],{"class":187}," next",[173,1347,317],{"class":301},[173,1349,1350],{"class":179}," root.dataset.theme ",[173,1352,354],{"class":301},[173,1354,357],{"class":208},[173,1356,1357],{"class":301}," ?",[173,1359,391],{"class":208},[173,1361,360],{"class":301},[173,1363,357],{"class":208},[173,1365,394],{"class":179},[173,1367,1369,1371,1374,1376,1379,1381],{"class":175,"line":1368},74,[173,1370,1342],{"class":301},[173,1372,1373],{"class":323}," apply",[173,1375,317],{"class":301},[173,1377,1378],{"class":179}," () ",[173,1380,1334],{"class":301},[173,1382,305],{"class":179},[173,1384,1386,1389,1391],{"class":175,"line":1385},75,[173,1387,1388],{"class":179},"        root.dataset.theme ",[173,1390,205],{"class":301},[173,1392,1393],{"class":179}," next;\n",[173,1395,1397,1400,1402,1404,1406,1408,1410,1413,1415,1417],{"class":175,"line":1396},76,[173,1398,1399],{"class":179},"        btn.",[173,1401,1285],{"class":323},[173,1403,327],{"class":179},[173,1405,1290],{"class":208},[173,1407,627],{"class":179},[173,1409,1295],{"class":323},[173,1411,1412],{"class":179},"(next ",[173,1414,354],{"class":301},[173,1416,357],{"class":208},[173,1418,1305],{"class":179},[173,1420,1422,1425,1428,1431,1433,1435,1438,1440],{"class":175,"line":1421},77,[173,1423,1424],{"class":301},"        try",[173,1426,1427],{"class":179}," { localStorage.",[173,1429,1430],{"class":323},"setItem",[173,1432,327],{"class":179},[173,1434,330],{"class":208},[173,1436,1437],{"class":179},", next); } ",[173,1439,403],{"class":301},[173,1441,406],{"class":179},[173,1443,1445],{"class":175,"line":1444},78,[173,1446,1447],{"class":179},"      };\n",[173,1449,1451],{"class":175,"line":1450},79,[173,1452,572],{"emptyLinePlaceholder":571},[173,1454,1456],{"class":175,"line":1455},80,[173,1457,1458],{"class":294},"      \u002F\u002F Centre the reveal on the button.\n",[173,1460,1462,1464,1467,1469,1472,1475],{"class":175,"line":1461},81,[173,1463,1342],{"class":301},[173,1465,1466],{"class":187}," r",[173,1468,317],{"class":301},[173,1470,1471],{"class":179}," btn.",[173,1473,1474],{"class":323},"getBoundingClientRect",[173,1476,1477],{"class":179},"();\n",[173,1479,1481,1484,1487,1489,1492,1494,1497,1500,1503,1506,1509,1511,1513,1516,1519,1522,1525],{"class":175,"line":1480},82,[173,1482,1483],{"class":179},"      root.style.",[173,1485,1486],{"class":323},"setProperty",[173,1488,327],{"class":179},[173,1490,1491],{"class":208},"'--x'",[173,1493,627],{"class":179},[173,1495,1496],{"class":208},"`${",[173,1498,1499],{"class":179},"r",[173,1501,1502],{"class":208},".",[173,1504,1505],{"class":179},"left",[173,1507,1508],{"class":301}," +",[173,1510,1466],{"class":179},[173,1512,1502],{"class":208},[173,1514,1515],{"class":179},"width",[173,1517,1518],{"class":301}," \u002F",[173,1520,1521],{"class":187}," 2",[173,1523,1524],{"class":208},"}px`",[173,1526,333],{"class":179},[173,1528,1530,1532,1534,1536,1539,1541,1543,1545,1547,1550,1552,1554,1556,1559,1561,1563,1565],{"class":175,"line":1529},83,[173,1531,1483],{"class":179},[173,1533,1486],{"class":323},[173,1535,327],{"class":179},[173,1537,1538],{"class":208},"'--y'",[173,1540,627],{"class":179},[173,1542,1496],{"class":208},[173,1544,1499],{"class":179},[173,1546,1502],{"class":208},[173,1548,1549],{"class":179},"top",[173,1551,1508],{"class":301},[173,1553,1466],{"class":179},[173,1555,1502],{"class":208},[173,1557,1558],{"class":179},"height",[173,1560,1518],{"class":301},[173,1562,1521],{"class":187},[173,1564,1524],{"class":208},[173,1566,333],{"class":179},[173,1568,1570],{"class":175,"line":1569},84,[173,1571,572],{"emptyLinePlaceholder":571},[173,1573,1575,1578],{"class":175,"line":1574},85,[173,1576,1577],{"class":301},"      if",[173,1579,1580],{"class":179}," (document.startViewTransition) {\n",[173,1582,1584,1587,1590],{"class":175,"line":1583},86,[173,1585,1586],{"class":179},"        document.",[173,1588,1589],{"class":323},"startViewTransition",[173,1591,1592],{"class":179},"(apply);\n",[173,1594,1596,1599,1602],{"class":175,"line":1595},87,[173,1597,1598],{"class":179},"      } ",[173,1600,1601],{"class":301},"else",[173,1603,305],{"class":179},[173,1605,1607,1610,1613,1615,1618],{"class":175,"line":1606},88,[173,1608,1609],{"class":179},"        root.classList.",[173,1611,1612],{"class":323},"add",[173,1614,327],{"class":179},[173,1616,1617],{"class":208},"'theme-fading'",[173,1619,333],{"class":179},[173,1621,1623,1626],{"class":175,"line":1622},89,[173,1624,1625],{"class":323},"        apply",[173,1627,1477],{"class":179},[173,1629,1631,1634,1637,1639,1642,1645,1647,1649,1652,1655],{"class":175,"line":1630},90,[173,1632,1633],{"class":323},"        setTimeout",[173,1635,1636],{"class":179},"(() ",[173,1638,1334],{"class":301},[173,1640,1641],{"class":179}," root.classList.",[173,1643,1644],{"class":323},"remove",[173,1646,327],{"class":179},[173,1648,1617],{"class":208},[173,1650,1651],{"class":179},"), ",[173,1653,1654],{"class":187},"300",[173,1656,333],{"class":179},[173,1658,1660],{"class":175,"line":1659},91,[173,1661,1662],{"class":179},"      }\n",[173,1664,1666],{"class":175,"line":1665},92,[173,1667,1668],{"class":179},"    });\n",[173,1670,1672,1675,1677],{"class":175,"line":1671},93,[173,1673,1674],{"class":179},"  \u003C\u002F",[173,1676,286],{"class":183},[173,1678,191],{"class":179},[173,1680,1682,1684,1686],{"class":175,"line":1681},94,[173,1683,412],{"class":179},[173,1685,1152],{"class":183},[173,1687,191],{"class":179},[173,1689,1691,1693,1695],{"class":175,"line":1690},95,[173,1692,412],{"class":179},[173,1694,168],{"class":183},[173,1696,191],{"class":179},[14,1698,1699,1700,1703],{},"The ",[18,1701,1702],{},"theme-fading"," class is the detail that separates a good fallback from a bad one. The colour transition exists only while the switch is happening, so the rest of the time hovers and focus states change at their own speed. Without the class, the surfaces would crossfade on any colour change, including ones unrelated to theming.",[34,1705,1707],{"id":1706},"the-key-technique-animating-snapshots-not-elements","The key technique: animating snapshots, not elements",[14,1709,1710,1711,1714,1715,1718],{},"A view transition works in three steps. The browser captures the current page as an image, the callback applies the DOM change, and the browser captures the new state. It then renders a pseudo-element tree — ",[18,1712,1713],{},"::view-transition-old(root)"," showing the old image and ",[18,1716,1717],{},"::view-transition-new(root)"," showing the new one — and animates them. Because these are images, the animation costs the same whether the page has ten elements or ten thousand.",[50,1720,57,1722,57,1725,57,1728,57,1731,57,1735,57,1740,57,1744,57,1748,57,1752,57,1756,57,1760,57,1765,57,1768,57,1772,57,1777],{"viewBox":52,"role":53,"ariaLabel":1721,"xmlns":55,"style":56},"Sequence of a view transition theme switch: capture old snapshot, apply theme, capture new snapshot, then clip the new snapshot with a growing circle from the toggle",[59,1723,1724],{},"View transition steps for a circular reveal",[63,1726,1727],{},"Step one captures the light page. Step two applies the dark theme. Step three captures the dark page. Step four draws the old snapshot underneath and the new snapshot on top, clipped to a circle that grows from the toggle position until it covers the screen.",[67,1729,1730],{"textAnchor":69,"x":70,"y":71,"style":72},"Old image under, new image revealed on top",[75,1732],{"x":77,"y":1733,"width":974,"height":94,"rx":1734,"fill":82,"stroke":83,"strokeWidth":84,"opacity":85},"56","8",[67,1736,1739],{"textAnchor":69,"x":1737,"y":1738,"style":95},"95","116","1. snapshot old",[75,1741],{"x":1742,"y":1733,"width":974,"height":94,"rx":1734,"fill":113,"opacity":1743},"195","0.14",[67,1745,1747],{"textAnchor":69,"x":1746,"y":1738,"style":95},"270","2. apply theme",[75,1749],{"x":1750,"y":1733,"width":974,"height":94,"rx":1734,"fill":83,"opacity":1751},"370","0.25",[67,1753,1755],{"textAnchor":69,"x":1754,"y":1738,"style":95},"445","3. snapshot new",[75,1757],{"x":1758,"y":1733,"width":1759,"height":94,"rx":1734,"fill":82,"stroke":83,"strokeWidth":84,"opacity":85},"545","155",[913,1761],{"cx":1762,"cy":1763,"r":1764,"fill":83,"opacity":1751},"570","80","60",[913,1766],{"cx":1762,"cy":1763,"r":1767,"fill":113},"6",[67,1769,1771],{"textAnchor":69,"x":1770,"y":974,"style":95},"640","4. reveal",[67,1773,1776],{"textAnchor":69,"x":70,"y":1774,"style":1775},"210","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace","::view-transition-new(root) { clip-path: circle(r at x y) }",[67,1778,1780],{"textAnchor":69,"x":70,"y":1779,"style":95},"244","The page underneath is not animating at all: only one clipped image grows.",[14,1782,1783,1784,1787,1788,1790,1791,1794,1795,1798,1799,1502],{},"The default view transition is a crossfade. Setting ",[18,1785,1786],{},"animation: none"," on both snapshot pseudo-elements and then giving the new one a ",[18,1789,908],{}," animation replaces the crossfade with the reveal. ",[18,1792,1793],{},"mix-blend-mode: normal"," removes the default blending the browser applies during the crossfade, so the new image is drawn opaquely on top of the old. The radius of ",[18,1796,1797],{},"150%"," is generous enough to cover the far corner of the viewport from any starting point. For more on the pseudo-element tree, see ",[23,1800,1802],{"href":1801},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions\u002F","Same-Document View Transitions",[34,1804,1806],{"id":1805},"details-that-make-the-switch-feel-finished","Details that make the switch feel finished",[14,1808,1809],{},"A few small decisions separate a polished theme switch from one that merely works.",[14,1811,1812,1816,1817,1820,1821,1824,1825,1827,1828,1830],{},[1813,1814,1815],"strong",{},"Images and illustrations."," Photographs usually look fine in both themes, but illustrations drawn on white, logos with dark text and screenshots of light interfaces glare on a dark page. Swap them with ",[18,1818,1819],{},"\u003Cpicture>"," and ",[18,1822,1823],{},"media=\"(prefers-color-scheme: dark)\""," where the theme follows the system, or with a ",[18,1826,505],{}," selector on a background image where a toggle is used. Inline SVGs that use ",[18,1829,83],{}," adapt automatically, which is one reason to prefer them for diagrams.",[14,1832,1833,1836],{},[1813,1834,1835],{},"Third-party embeds."," Maps, video players and comment widgets inside iframes do not inherit the page's theme. Many accept a theme parameter; those that do not will stay light, so frame them with a border so the contrast change reads as intentional.",[14,1838,1839,1842,1843,1846,1847,1850,1851,1854],{},[1813,1840,1841],{},"The browser's own UI."," The ",[18,1844,1845],{},"color-scheme"," property themes scrollbars, form controls and the default canvas, and the ",[18,1848,1849],{},"theme-color"," meta tag tints the mobile browser toolbar. Update the meta tag's content during the switch, or provide two tags with ",[18,1852,1853],{},"media"," attributes for light and dark, so the toolbar does not stay bright above a dark page.",[14,1856,1857,1860],{},[1813,1858,1859],{},"Timing."," Keep the reveal under half a second. A theme switch is a preference change, not an event worth celebrating, and users who toggle back and forth to compare themes will find a long reveal tiresome.",[34,1862,1864],{"id":1863},"accessibility-notes","Accessibility notes",[14,1866,1867,1868,1871,1872,1875,1876,1879,1880,1502],{},"Three things keep the switch accessible. The toggle is a real ",[18,1869,1870],{},"\u003Cbutton>"," with ",[18,1873,1874],{},"aria-pressed",", so screen readers announce \"Dark theme, toggle button, pressed\". The choice is persisted so it survives reloads, and the inline head script applies it before paint — a flash of a bright theme when a user has chosen dark is not merely cosmetic for people with light sensitivity. And the circular reveal is replaced with a short fade under ",[18,1877,1878],{},"prefers-reduced-motion",", following the approach in ",[23,1881,1883],{"href":1882},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion\u002F","View Transitions With Reduced Motion",[34,1885,1887,1888],{"id":1886},"variation-a-pure-css-toggle-with-has","Variation: a pure-CSS toggle with ",[18,1889,1890],{},":has()",[14,1892,1893,1894,1896],{},"If the only goal is a crossfade and the preference need not persist, the toggle can be a checkbox and the theme selected with ",[18,1895,1890],{},", with no script at all.",[164,1898,1902],{"className":1899,"code":1900,"language":1901,"meta":169,"style":169},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast",":root:has(#theme-switch:checked) {\n  --surface: #0f172a;\n  --text: #e2e8f0;\n  --card: #1e293b;\n  color-scheme: dark;\n}\n\nbody, .card {\n  transition: background-color 250ms ease, color 250ms ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  body, .card { transition: none; }\n}\n","css",[18,1903,1904,1917,1928,1939,1950,1961,1966,1970,1981,2006,2010,2014,2021,2040],{"__ignoreMap":169},[173,1905,1906,1909,1911,1914],{"class":175,"line":176},[173,1907,1908],{"class":187},":root:has",[173,1910,327],{"class":179},[173,1912,1913],{"class":187},"#theme-switch:checked",[173,1915,1916],{"class":179},") {\n",[173,1918,1919,1922,1924,1926],{"class":175,"line":194},[173,1920,1921],{"class":440},"  --surface",[173,1923,444],{"class":179},[173,1925,460],{"class":187},[173,1927,394],{"class":179},[173,1929,1930,1933,1935,1937],{"class":175,"line":214},[173,1931,1932],{"class":440},"  --text",[173,1934,444],{"class":179},[173,1936,534],{"class":187},[173,1938,394],{"class":179},[173,1940,1941,1944,1946,1948],{"class":175,"line":224},[173,1942,1943],{"class":440},"  --card",[173,1945,444],{"class":179},[173,1947,546],{"class":187},[173,1949,394],{"class":179},[173,1951,1952,1955,1957,1959],{"class":175,"line":242},[173,1953,1954],{"class":187},"  color-scheme",[173,1956,444],{"class":179},[173,1958,558],{"class":187},[173,1960,394],{"class":179},[173,1962,1963],{"class":175,"line":267},[173,1964,1965],{"class":179},"}\n",[173,1967,1968],{"class":175,"line":281},[173,1969,572],{"emptyLinePlaceholder":571},[173,1971,1972,1974,1976,1979],{"class":175,"line":291},[173,1973,1152],{"class":183},[173,1975,627],{"class":179},[173,1977,1978],{"class":187},".card",[173,1980,305],{"class":179},[173,1982,1983,1986,1988,1990,1992,1994,1996,1998,2000,2002,2004],{"class":175,"line":298},[173,1984,1985],{"class":187},"  transition",[173,1987,755],{"class":179},[173,1989,112],{"class":187},[173,1991,760],{"class":301},[173,1993,763],{"class":187},[173,1995,627],{"class":179},[173,1997,651],{"class":187},[173,1999,770],{"class":187},[173,2001,760],{"class":301},[173,2003,763],{"class":187},[173,2005,394],{"class":179},[173,2007,2008],{"class":175,"line":308},[173,2009,1965],{"class":179},[173,2011,2012],{"class":175,"line":336},[173,2013,572],{"emptyLinePlaceholder":571},[173,2015,2016,2019],{"class":175,"line":374},[173,2017,2018],{"class":301},"@media",[173,2020,1031],{"class":179},[173,2022,2023,2025,2027,2029,2031,2034,2036,2038],{"class":175,"line":397},[173,2024,578],{"class":183},[173,2026,627],{"class":179},[173,2028,1978],{"class":187},[173,2030,581],{"class":179},[173,2032,2033],{"class":187},"transition",[173,2035,444],{"class":179},[173,2037,82],{"class":187},[173,2039,1052],{"class":179},[173,2041,2042],{"class":175,"line":409},[173,2043,1965],{"class":179},[14,2045,2046,2047,2050],{},"The demo applies this version to a wrapper element rather than ",[18,2048,2049],{},":root",", because the demo frame is a small embedded document; the mechanism is identical.",[2052,2053],"live-demo",{"name":2054},"theme-switch-crossfade",[14,2056,2057],{},"The trade-offs are real: the choice resets on reload, the transition applies to all colour changes on those surfaces, and there is no view transition without script. It suits demos and single-page tools more than production sites, but it shows how little of the effect genuinely requires JavaScript.",[34,2059,2061],{"id":2060},"browser-support","Browser support",[14,2063,2064,2065,2068,2069,2072,2073,2075,2076,2078,2079,2081],{},"Same-document view transitions (",[18,2066,2067],{},"document.startViewTransition()"," and the ",[18,2070,2071],{},"::view-transition-*"," pseudo-elements) are supported in Chrome and Edge 111+, Firefox 144+ and Safari 18+; elsewhere the scoped crossfade fallback runs. Colour transitions and ",[18,2074,1845],{}," are universal. ",[18,2077,1890],{}," for the script-free variant is supported in Chrome and Edge 105+, Firefox 121+ and Safari 15.4+. ",[18,2080,1878],{}," is supported in Chrome 74+, Edge 79+, Firefox 63+ and Safari 10.1+.",[34,2083,2085],{"id":2084},"faq","FAQ",[14,2087,2088,2091],{},[1813,2089,2090],{},"Why does my page flash the wrong theme on load?","\nThe stored theme is applied after the first paint, usually by a script at the end of the body or in a framework's mount hook. Apply it in a tiny inline script in the head, before any styles render, so the first frame already uses the correct theme.",[14,2093,2094,2097],{},[1813,2095,2096],{},"Should I put a transition on every element for the theme change?","\nNo. A universal selector with a colour transition makes every element animate on every hover and state change too, and the thousands of simultaneous transitions during a theme switch can drop frames. Transition only the large surfaces, or use a view transition that animates one snapshot.",[14,2099,2100,2103,2106],{},[1813,2101,2102],{},"How does a view transition animate a theme switch?",[18,2104,2105],{},"document.startViewTransition"," takes a snapshot of the page, applies the theme change, takes another snapshot, and animates between the two images. Because it animates two flat images rather than every element's colour, it is cheap and allows effects such as a circular reveal from the toggle button.",[14,2108,2109,2112],{},[1813,2110,2111],{},"What should reduced-motion users see when switching themes?","\nA near-instant change, or a very short crossfade. The circular wipe and any sweeping reveal count as motion. A brief opacity crossfade is generally acceptable because it involves no movement.",[34,2114,2116],{"id":2115},"related","Related",[2118,2119,2120,2126,2133,2140],"ul",{},[2121,2122,2123,2125],"li",{},[23,2124,26],{"href":25}," — the parent guide.",[2121,2127,2128,2132],{},[23,2129,2131],{"href":2130},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Flight-dark-function-for-themes\u002F","Light and Dark Themes With light-dark()"," — declaring the tokens being switched.",[2121,2134,2135,2139],{},[23,2136,2138],{"href":2137},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002F","View Transitions for CSS Developers"," — the snapshot mechanism in depth.",[2121,2141,2142,2146],{},[23,2143,2145],{"href":2144},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcontainer-style-query-theming\u002F","Container Style Query Theming"," — themes applied to sections rather than pages.",[424,2148,2149],{},"html pre.shiki code .suds8, html code.shiki .suds8{--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sne4z, html code.shiki .sne4z{--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .sT6z2, html code.shiki .sT6z2{--shiki-default:#032563;--shiki-dark:#ADDCFF}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}html pre.shiki code .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .sKwhi, html code.shiki .sKwhi{--shiki-default:#622CBC;--shiki-dark:#DBB7FF}html pre.shiki code .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":169,"searchDepth":194,"depth":194,"links":2151},[2152,2153,2154,2155,2156,2157,2159,2160,2161],{"id":36,"depth":194,"text":37},{"id":158,"depth":194,"text":159},{"id":1706,"depth":194,"text":1707},{"id":1805,"depth":194,"text":1806},{"id":1863,"depth":194,"text":1864},{"id":1886,"depth":194,"text":2158},"Variation: a pure-CSS toggle with :has()",{"id":2060,"depth":194,"text":2061},{"id":2084,"depth":194,"text":2085},{"id":2115,"depth":194,"text":2116},"Make a theme toggle feel deliberate: a scoped colour crossfade, a circular view-transition reveal from the toggle, no flash on load, and reduced-motion support.","md",{"pageTitle":2165,"datePublished":2166,"dateModified":2166,"faq":2167},"Smooth Light\u002FDark Theme Switch Transitions","2026-09-18",[2168,2170,2172,2174],{"q":2090,"a":2169},"The stored theme is applied after the first paint, usually by a script at the end of the body or in a framework's mount hook. Apply it in a tiny inline script in the head, before any styles render, so the first frame already uses the correct theme.",{"q":2096,"a":2171},"No. A universal selector with a colour transition makes every element animate on every hover and state change too, and the thousands of simultaneous transitions during a theme switch can drop frames. Transition only the large surfaces, or use a view transition that animates one snapshot.",{"q":2102,"a":2173},"document.startViewTransition takes a snapshot of the page, applies the theme change, takes another snapshot, and animates between the two images. Because it animates two flat images rather than every element's colour, it is cheap and allows effects such as a circular reveal from the toggle button.",{"q":2111,"a":2175},"A near-instant change, or a very short crossfade. The circular wipe and any sweeping reveal count as motion. A brief opacity crossfade is generally acceptable because it involves no movement.","\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions",{"title":5,"description":2162},"css-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions\u002Findex","kuBNwjIOMAPFDcVi1ljoEJHzg7Rzrn3Fs2GwDRgp_ik",[2181,2184,2187,2190,2193,2196,2199,2202,2205,2208,2211,2214,2217,2220,2223,2226,2229,2232,2235,2238,2239,2242,2245,2248,2251,2254,2257,2260,2263,2266,2269,2272,2275,2278,2281,2284,2287,2290,2293,2296,2299,2302,2305,2308,2311,2314,2317,2320,2323,2326,2329,2332,2335,2338,2341,2344,2347,2350,2353,2356,2359,2362,2365,2368,2371,2374,2377,2380,2383,2386,2389,2392,2395,2398,2401,2404,2407,2410,2413,2416,2419,2422,2425,2428,2431,2434,2437,2440,2443,2446,2449,2452,2455,2458,2460,2463,2466,2469,2472,2475,2478,2481,2484,2487,2490,2493,2496,2499,2502,2505,2508,2511,2514,2517,2520,2523,2526,2529,2532,2535,2538,2541,2544,2547,2550,2553,2556,2559,2562,2565,2568,2571,2574,2577,2580,2583,2586,2589,2592,2595,2598,2601,2604,2607,2610,2613,2616,2619,2622,2625,2628,2631,2634,2637,2640,2643,2646,2649,2652,2655,2658,2661,2664,2667,2670,2673,2676,2679,2682,2685,2688,2691,2694,2697,2700,2703,2706,2709,2712,2715,2718,2721,2724,2727,2730,2733,2736],{"path":2182,"title":2183},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators","Creating Accessible Focus Indicators: Building a Ring That Survives Any Background",{"path":2185,"title":2186},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fflashing-content-and-seizure-thresholds","Flashing Content and Seizure Thresholds",{"path":2188,"title":2189},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fforced-colors-mode-and-animations","Forced Colors Mode and Animations",{"path":2191,"title":2192},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations","Accessibility in CSS Animations: Patterns, Specs & Best Practices",{"path":2194,"title":2195},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fpause-controls-for-looping-animations","Pause Controls for Looping CSS Animations: Meeting WCAG 2.2.2",{"path":2197,"title":2198},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes","prefers-reduced-motion Recipes: A Reduction for Every Common Effect",{"path":2200,"title":2201},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css","Reducing Motion Preferences in CSS: How the Setting Becomes a Media Feature",{"path":2203,"title":2204},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Ftarget-size-and-pointer-accessibility","Target Size and Pointer Accessibility: Meeting 2.5.8 Without Redesigning",{"path":2206,"title":2207},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns","Vestibular-Safe Animation Patterns: Why Some Motion Makes People Sick",{"path":2209,"title":2210},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fwcag-motion-success-criteria","WCAG Motion Success Criteria: What Each One Actually Requires",{"path":2212,"title":2213},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fanimated-gradient-borders-with-property","Animated Gradient Borders With @property: A Spinning Highlight Without Extra Elements",{"path":2215,"title":2216},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fclip-path-reveal-animations","Clip-Path Reveal Animations: Wipes, Irises and Corners Without Extra Markup",{"path":2218,"title":2219},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations","Clip-Path & Mask Animations: Animating Visibility Instead of Position",{"path":2221,"title":2222},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fmask-image-fade-edges","Fading Edges With mask-image: Soft Boundaries That Work on Any Background",{"path":2224,"title":2225},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations\u002Fmorphing-shapes-with-clip-path-polygon","Morphing Shapes With clip-path: polygon(): Point-Matched Transitions",{"path":2227,"title":2228},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fanimating-gradients","Animating CSS Gradients: Smooth Colour Motion Without Jumps",{"path":2230,"title":2231},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fcolor-mix-hover-states","Hover and Active States With color-mix(): One Token, Every Shade",{"path":2233,"title":2234},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions","Color & Theme Transitions: Colour as Part of the Motion System",{"path":2236,"title":2237},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Flight-dark-function-for-themes","Light and Dark Themes With light-dark(): One Declaration per Colour",{"path":2176,"title":5},{"path":2240,"title":2241},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fcontainer-query-hover-affordances","Container Query Hover Affordances: When Narrow Means Persistent, Not Hidden",{"path":2243,"title":2244},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion","Container-Aware Motion: Animation Sized by the Space a Component Actually Has",{"path":2246,"title":2247},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Flayout-change-animations-at-container-breakpoints","Animating Changes at Container Breakpoints",{"path":2249,"title":2250},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fmotion-driven-by-container-style-queries","Motion Driven by Container Style Queries",{"path":2252,"title":2253},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fmotion-that-scales-with-container-size","Motion That Scales with Container Size: cqi, cqb, and Derived Durations",{"path":2255,"title":2256},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fsuppressing-motion-in-small-containers","Suppressing Motion in Small Containers: Deliberately Animating Nothing",{"path":2258,"title":2259},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fcontrolling-css-animations-from-javascript","Controlling CSS Animations From JavaScript",{"path":2261,"title":2262},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api","CSS Animation vs the Web Animations API: A Decision Guide",{"path":2264,"title":2265},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwaapi-vs-css-for-interruptible-ui","Interruptible UI: CSS Transitions vs WAAPI",{"path":2267,"title":2268},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation","When to Use JavaScript Instead of CSS for Animation",{"path":2270,"title":2271},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property","Animating Custom Properties with @property: Typed Variables That Interpolate",{"path":2273,"title":2274},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fcustom-property-fallbacks-and-invalid-values","Custom Property Fallbacks and Invalid Values",{"path":2276,"title":2277},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations","Fluid Spacing Tokens That Drive Transition Durations",{"path":2279,"title":2280},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture","CSS Custom Properties Architecture: Scalable Design Systems & Dynamic UI",{"path":2282,"title":2283},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fregistered-properties-and-type-safety","Registered Properties and Type Safety: @property as a Contract for Design Tokens",{"path":2285,"title":2286},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fscoping-custom-properties-to-components","Scoping Custom Properties to Components",{"path":2288,"title":2289},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions","CSS Transition Timing Functions: ease, cubic-bezier(), steps() and linear()",{"path":2291,"title":2292},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals","CSS Transition Fundamentals: Architecture, Performance & Patterns",{"path":2294,"title":2295},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Finterrupted-and-reversing-transitions","Interrupted and Reversing Transitions: What Happens Mid-Flight",{"path":2297,"title":2298},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fstarting-style-entry-animations","@starting-style: Animating an Element's Very First Style Change",{"path":2300,"title":2301},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-delay-choreography","Choreographing Motion With transition-delay",{"path":2303,"title":2304},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-shorthand-and-multiple-properties","The transition Shorthand and Multiple Properties",{"path":2306,"title":2307},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete","Transitioning display with transition-behavior: allow-discrete and @starting-style",{"path":2309,"title":2310},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fanimating-dialog-open-and-close","Animating \u003Cdialog> Open and Close: Both Directions, No Library",{"path":2312,"title":2313},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fbackdrop-animations","Animating ::backdrop: Fades and Blurs Behind Dialogs and Popovers",{"path":2315,"title":2316},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations","Dialog & Popover Animations: Motion for the Top Layer",{"path":2318,"title":2319},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Finterpolate-size-for-height-auto-animation","Animating to height: auto With interpolate-size and calc-size()",{"path":2321,"title":2322},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fpopover-entry-and-exit-transitions","Popover Menus That Animate From Their Anchor",{"path":2324,"title":2325},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fchoosing-animation-durations","Choosing Animation Durations: How Long Motion Should Last",{"path":2327,"title":2328},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fcubic-bezier-overshoot-and-anticipation","Overshoot and Anticipation With cubic-bezier(): Motion With Character",{"path":2330,"title":2331},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fentry-vs-exit-easing","Entry vs Exit Easing: Different Curves for Arriving and Leaving",{"path":2333,"title":2334},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design","Easing & Motion Design: Making CSS Motion Feel Intentional",{"path":2336,"title":2337},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Flinear-easing-function-for-springs","Spring and Bounce Easing With linear(): Physical Motion Without JavaScript",{"path":2339,"title":2340},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fsteps-easing-for-sprite-animations","steps() Easing: Sprite Sheets, Typewriters and Other Frame-by-Frame Effects",{"path":2342,"title":2343},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips","Accessible CSS-Only Tooltips and Hover Cards With  and ",{"path":2345,"title":2346},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fbutton-press-and-active-states","Button Press and  States",{"path":2348,"title":2349},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives"," vs :focus: Why It Replaced the Focus-Ring Polyfill",{"path":2351,"title":2352},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-within-form-patterns"," Form Patterns: Styling a Group From the Field Inside It",{"path":2354,"title":2355},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fhover-intent-delays-with-transition-delay","Hover Intent Delays With transition-delay: Menus That Don't Flicker",{"path":2357,"title":2358},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design","Hover & Focus State Design: Spec-Compliant Patterns for Modern UIs",{"path":2360,"title":2361},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fpointer-and-hover-media-queries","Pointer and Hover Media Queries: Gating Affordances by Input Capability",{"path":2363,"title":2364},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript","Smooth Hover Effects Without JavaScript: CSS-Only Patterns for Modern UI",{"path":2366,"title":2367},"\u002Fcss-only-micro-interactions-animations","CSS-Only Micro-Interactions & Animations: Architecture, Performance & Implementation",{"path":2369,"title":2370},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fanimation-composition-and-stacking","Stacking Animations With animation-composition",{"path":2372,"title":2373},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fanimation-fill-mode-explained","animation-fill-mode: What an Animation Applies Before and After It Runs",{"path":2375,"title":2376},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations","Container-Query-Triggered Keyframe Animations: Animate Differently per Available Space",{"path":2378,"title":2379},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-accordions-and-disclosure","CSS-Only Accordions and Disclosure Widgets with details and summary",{"path":2381,"title":2382},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons","CSS-Only Loading Spinners and Skeleton Shimmer Screens with @keyframes",{"path":2384,"title":2385},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes","CSS-Only Toggle Switches and Checkboxes That Keep the Native Input",{"path":2387,"title":2388},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns","Keyframe Animation Patterns: Spec-Compliant Architectures for Modern UIs",{"path":2390,"title":2391},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fmulti-step-keyframes-and-per-keyframe-easing","Multi-Step Keyframes and Per-Keyframe Easing",{"path":2393,"title":2394},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties","Staggered List Animations Using --i Index Custom Properties",{"path":2396,"title":2397},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fanimating-box-shadow-and-filters-cheaply","Animating box-shadow and Filters Cheaply",{"path":2399,"title":2400},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcontain-property-for-animation-performance","The contain Property for Animation Performance",{"path":2402,"title":2403},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcss-triggers-layout-paint-composite","Layout, Paint and Composite: What Triggers What",{"path":2405,"title":2406},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration","Performance & GPU Acceleration in CSS: A Developer’s Blueprint",{"path":2408,"title":2409},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps","Optimizing CSS Animations for 60fps: Budgeting the Frame",{"path":2411,"title":2412},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fprofiling-animations-in-devtools","Profiling CSS Animations in DevTools: Finding the Frame That Costs Too Much",{"path":2414,"title":2415},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread","will-change and the Compositor Thread: Layer Promotion Without the Footguns",{"path":2417,"title":2418},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations","Scroll-Driven Animations: Binding Motion to Scroll Position in Pure CSS",{"path":2420,"title":2421},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-animation-fallbacks","Shipping Scroll-Driven Animations Without Breaking Anything",{"path":2423,"title":2424},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-parallax-effects","Scroll-Driven Parallax Effects in CSS",{"path":2426,"title":2427},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-progress-bar-without-javascript","A Reading Progress Bar Driven by scroll(root)",{"path":2429,"title":2430},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-triggered-reveal-animations","Reveal-on-Scroll with view() and animation-range",{"path":2432,"title":2433},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Ftimeline-scope-for-cross-element-animations","timeline-scope for Cross-Element Animations",{"path":2435,"title":2436},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fview-timeline-and-animation-range","View Timelines and animation-range",{"path":2438,"title":2439},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fcross-document-view-transitions","Cross-Document View Transitions",{"path":2441,"title":2442},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers","View Transitions for CSS Developers: the Pseudo-Element Tree, Snapshots, and Named Elements",{"path":2444,"title":2445},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions","Writing a Same-Document View Transition: the CSS You Actually Author",{"path":2447,"title":2448},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-names-shared-elements","view-transition-name and Shared Elements: Morphing a Thumbnail into a Hero",{"path":2450,"title":2451},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-types-and-classes","View Transition Types and Classes",{"path":2453,"title":2454},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-for-list-reordering","View Transitions for List Reordering",{"path":2456,"title":2457},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion","Honouring prefers-reduced-motion in View Transitions",{"path":618,"title":2459},"Modern CSS Layouts & Micro-Interactions",{"path":2461,"title":2462},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioned-tooltips","Anchor-Positioned Tooltips: Tethering, Flipping, and Getting the Semantics Right",{"path":2464,"title":2465},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioning-fallbacks","Anchor Positioning Fallbacks: A Graceful Floor for Older Browser Versions",{"path":2467,"title":2468},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-size-for-matching-widths","anchor-size(): Overlays That Match Their Trigger's Size",{"path":2470,"title":2471},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays","CSS Anchor Positioning and Overlays: Tethering Elements Without JavaScript",{"path":2473,"title":2474},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-attribute-and-css-styling","The popover Attribute: Top-Layer Overlays With No JavaScript",{"path":2476,"title":2477},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-light-dismiss-and-stacking","Popover Light Dismiss, Nesting and Stacking",{"path":2479,"title":2480},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fposition-area-grid-explained","position-area: The Anchor Placement Grid",{"path":2482,"title":2483},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fcontainer-query-polyfill-tradeoffs","Container Query Polyfills: What They Cost and When to Skip Them",{"path":2485,"title":2486},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports","Feature Detection with @supports: Syntax, Semantics, and the Traps",{"path":2488,"title":2489},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers","Handling Container Query Fallbacks for Older Browsers",{"path":2491,"title":2492},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks","Container Query Fallbacks: Spec-Compliant CSS Strategies for Legacy Browsers",{"path":2494,"title":2495},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fintrinsic-layout-fallbacks-without-queries","Intrinsic Layout Fallbacks: Baselines That Adapt Without Any Query",{"path":2497,"title":2498},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ftesting-fallbacks-without-old-browsers","Testing CSS Fallbacks Without an Old Browser",{"path":2500,"title":2501},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-queries-inside-css-nesting","Container Queries Inside CSS Nesting: Keeping Responses Next to the Styles They Change",{"path":2503,"title":2504},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-range-and-logic-operators","Container Query Range Syntax and Logic Operators",{"path":2506,"title":2507},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained","Container Query Units Explained: cqi, cqb, cqw, cqh, cqmin and cqmax",{"path":2509,"title":2510},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-type-size-vs-inline-size","Choosing container-type: inline-size, size, or normal",{"path":2512,"title":2513},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison","Container vs Media Queries: Choosing the Right Reference Box",{"path":2515,"title":2516},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fdebugging-container-queries-in-devtools","Debugging Container Queries in DevTools: Why a Rule Doesn't Match",{"path":2518,"title":2519},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production","How to Use Container Queries in Production Without a Rewrite",{"path":2521,"title":2522},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics","Container Query Syntax Basics",{"path":2524,"title":2525},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries","Nesting and Naming Container Queries: Targeting the Right Ancestor",{"path":2527,"title":2528},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fauto-fit-minmax-responsive-grids","Responsive Grids with repeat(auto-fit, minmax())",{"path":2530,"title":2531},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid","Building a Responsive Dashboard Where Cards Align with Subgrid",{"path":2533,"title":2534},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fgrid-auto-flow-dense-packing","grid-auto-flow: dense for Packed Layouts",{"path":2536,"title":2537},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fgrid-template-areas-responsive-layouts","Responsive Layouts With grid-template-areas: Rearranging Regions by Name",{"path":2539,"title":2540},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid","The Holy-Grail Layout with CSS Grid and Container Queries",{"path":2542,"title":2543},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts","CSS Grid and Subgrid Layouts for Responsive Interfaces",{"path":2545,"title":2546},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fmasonry-style-layouts-with-grid","Masonry-Style Layouts With the CSS That Ships Today",{"path":2548,"title":2549},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Foverlapping-elements-with-grid-stacking","Overlapping Elements With Grid Stacking: Layers Without position: absolute",{"path":2551,"title":2552},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fsubgrid-vs-nested-grid","Subgrid vs an Independently Nested Grid",{"path":2554,"title":2555},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fcentering-in-css-every-method","Centering in CSS: Every Modern Method and When Each One Breaks",{"path":2557,"title":2558},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-basis-vs-width","flex-basis vs width: Which Size a Flex Item Actually Starts From",{"path":2560,"title":2561},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-grow-and-flex-shrink-explained","flex-grow and flex-shrink Explained",{"path":2563,"title":2564},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-wrap-intrinsic-responsive-rows","Breakpoint-Free Responsive Rows With flex-wrap and flex-basis",{"path":2566,"title":2567},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflexbox-gap-and-spacing","Flexbox gap vs Margins: Spacing Items Without Edge Hacks",{"path":2569,"title":2570},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns","Flexbox Layout Patterns: One-Dimensional Layout for Real Components",{"path":2572,"title":2573},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fsticky-footer-with-flexbox-and-grid","Sticky Footer With Flexbox or Grid: Pinning the Footer on Short Pages",{"path":2575,"title":2576},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography","clamp() Fluid Type vs Media-Query Step Typography: Tradeoffs and Anatomy",{"path":2578,"title":2579},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fdebugging-clamp-values","Debugging clamp(): Values That Are Ignored, Stuck or Never Fluid",{"path":2581,"title":2582},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-line-height-and-measure","Fluid Line Height and Measure: Keeping Text Readable as It Scales",{"path":2584,"title":2585},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp","Building a Fluid Spacing Scale with clamp() and Container Units",{"path":2587,"title":2588},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-type-accessibility-and-zoom","Fluid Type, Accessibility and Zoom: The vw-Only Trap",{"path":2590,"title":2591},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript","Fluid Typography Without JavaScript: A Precision CSS Reference",{"path":2593,"title":2594},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp","Fluid Typography with clamp(): A Practical Guide for Modern CSS",{"path":2596,"title":2597},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ftext-wrap-balance-and-pretty","text-wrap: balance and pretty: Better Line Breaks Without Manual Breaks",{"path":2599,"title":2600},"\u002Fmastering-container-queries-responsive-layouts","Mastering Container Queries & Responsive Layouts",{"path":2602,"title":2603},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media","Using aspect-ratio for Responsive Media and Preventing Layout Shift",{"path":2605,"title":2606},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fcontent-visibility-and-contain-intrinsic-size","content-visibility and contain-intrinsic-size: Rendering Only What Is Near the Screen",{"path":2608,"title":2609},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques","Intrinsic Sizing Techniques: Modern CSS Layouts for Responsive UI",{"path":2611,"title":2612},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Flayout-widths-with-min-max-clamp","Layout Widths With min(), max() and clamp(): Sizing Without Breakpoints",{"path":2614,"title":2615},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained","min-content, max-content, and fit-content() Explained",{"path":2617,"title":2618},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fobject-fit-and-object-position","object-fit and object-position: Images That Fit Any Box",{"path":2620,"title":2621},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fpreventing-flex-and-grid-overflow","The min-width: auto Trap in Flex and Grid Layouts",{"path":2623,"title":2624},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens","Cascade Layers for Reset and Design Tokens",{"path":2626,"title":2627},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-vs-specificity-hacks","Cascade Layers vs Specificity Hacks: Ordering Instead of Escalating",{"path":2629,"title":2630},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcss-scope-for-component-styles","@scope for Component Styles: Short Selectors That Cannot Leak",{"path":2632,"title":2633},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies","Modern CSS Reset Strategies: A Spec-Compliant Foundation",{"path":2635,"title":2636},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Frevert-layer-and-revert-keywords","initial, inherit, unset, revert and revert-layer: Undoing Styles Correctly",{"path":2638,"title":2639},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fwriting-a-minimal-modern-reset","Writing a Minimal Modern CSS Reset: Every Line Justified",{"path":2641,"title":2642},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fcontent-aware-card-layouts-with-has","Content-Aware Card Layouts With :has(): One Component, Many Shapes",{"path":2644,"title":2645},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fempty-and-loading-states-with-has","Empty and Loading States With ()",{"path":2647,"title":2648},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fform-validation-layouts-with-has","Form Validation Layouts With :has(): Styling Groups From Field State",{"path":2650,"title":2651},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fhas-selector-performance",":has() Selector Performance: What Is Cheap, What Is Not, and How to Measure",{"path":2653,"title":2654},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fhas-vs-container-style-queries",":has() vs Container Style Queries: Discovered State and Published State",{"path":2656,"title":2657},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has","Parent-Aware Layouts With :has(): Letting Containers React to Their Contents",{"path":2659,"title":2660},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fquantity-queries-with-has","Quantity Queries With :has(): Layouts That Respond to How Many Items There Are",{"path":2662,"title":2663},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries","Building Responsive Cards with Container Queries: A Production-Ready Guide",{"path":2665,"title":2666},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables","Container Query Data Tables: Reflowing Tables to Cards",{"path":2668,"title":2669},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-sidebar-layouts","Container Query Sidebar Layouts That Travel Between Contexts",{"path":2671,"title":2672},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns","Responsive Component Patterns: Architecture & Implementation",{"path":2674,"title":2675},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-forms-with-container-queries","Responsive Forms with Container Queries: Reflow Without Losing the Wiring",{"path":2677,"title":2678},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-hero-sections-with-container-queries","Responsive Hero Sections With Container Queries",{"path":2680,"title":2681},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries","Responsive Navigation Without Media Queries Using Container Queries",{"path":2683,"title":2684},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-pricing-tables","Responsive Pricing Tables: Aligned Features, a Clear Recommendation, Any Width",{"path":2686,"title":2687},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Ftabs-that-become-accordions","Tabs That Become an Accordion: One Component, Two Layouts",{"path":2689,"title":2690},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fcss-only-carousel-with-scroll-snap","A CSS-Only Carousel With Scroll Snap",{"path":2692,"title":2693},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fhorizontal-scrolling-card-rows","Horizontal Scrolling Card Rows: Swipeable Lists That Stay in Their Lane",{"path":2695,"title":2696},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts","Scroll Snap & Overflow Layouts: Designing Components That Scroll on Purpose",{"path":2698,"title":2699},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Foverscroll-behavior-for-nested-scrolling","overscroll-behavior: Taming Nested Scrolling, Pull-to-Refresh and Back-Swipe",{"path":2701,"title":2702},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fscroll-margin-and-scroll-padding-for-sticky-headers","scroll-margin and scroll-padding: Keeping Targets Clear of Sticky Headers",{"path":2704,"title":2705},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcombining-size-and-style-queries","Combining Size and Style Container Queries",{"path":2707,"title":2708},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcontainer-style-query-theming","Container Style Query Theming: One Token Instead of a Variant Class Matrix",{"path":2710,"title":2711},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state","Style Queries and Container State: Token-Driven Component Variants",{"path":2713,"title":2714},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-queries-with-custom-properties","Style Queries With Custom Properties: Declaring, Inheriting, and Matching Tokens",{"path":2716,"title":2717},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-query-component-variants","Component Variants With Style Queries: One Token, Many Looks",{"path":2719,"title":2720},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-query-fallbacks-and-support","Style Query Fallbacks and Support: Designing for Older Browser Versions",{"path":2722,"title":2723},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fdvh-svh-lvh-mobile-viewport-units","dvh, svh and lvh: Choosing the Right Mobile Viewport Unit",{"path":2725,"title":2726},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Ffull-bleed-sections-in-constrained-layouts","Full-Bleed Sections in a Constrained Layout: Breaking Out Without Breaking the Page",{"path":2728,"title":2729},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout","Viewport Units & Mobile Layout: Designing for the Screen You Actually Get",{"path":2731,"title":2732},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fsafe-area-insets-for-notched-screens","Safe Area Insets: Designing Around Notches, Corners and Home Indicators",{"path":2734,"title":2735},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fviewport-units-vs-container-units","Viewport Units vs Container Units: Choosing the Right Reference Box",{"path":2737,"title":2738},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget","Layouts That Survive the Virtual Keyboard",{"title":2740,"caption":2741,"height":2742,"html":2743,"css":2744},"Script-free theme switch with a scoped crossfade","Tick the switch: only the panel surface and text crossfade over 250ms, while the rest of the frame is unaffected.",220,"\n\u003Cdiv class=\"app\">\n  \u003Clabel class=\"switch\">\u003Cinput type=\"checkbox\" id=\"theme-switch\"> Dark theme\u003C\u002Flabel>\n  \u003Cdiv class=\"card\">\u003Cp>Content that changes with the theme.\u003C\u002Fp>\u003C\u002Fdiv>\n\u003C\u002Fdiv>","\nbody { display: block; padding: 0; }\n.app {\n  --surface: #ffffff;\n  --text: #0f172a;\n  --card: #f1f5f9;\n  min-height: 100vh;\n  padding: 1.25rem;\n  background: var(--surface);\n  color: var(--text);\n  transition: background-color 250ms ease, color 250ms ease;\n}\n.app:has(#theme-switch:checked) {\n  --surface: #0f172a;\n  --text: #e2e8f0;\n  --card: #1e293b;\n  color-scheme: dark;\n}\n.card { margin-top: 1rem; padding: 1rem; border-radius: 12px; background: var(--card); transition: background-color 250ms ease; }\n.card p { margin: 0; }\n.switch { display: inline-flex; gap: 0.5rem; align-items: center; cursor: pointer; }\n.switch input:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }\n@media (prefers-reduced-motion: reduce) { .app, .card { transition: none; } }",1789717749288]