[{"data":1,"prerenderedAt":2269},["ShallowReactive",2],{"page-\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions\u002F":3,"content-all-pages":2006},{"id":4,"title":5,"body":6,"description":1988,"extension":1989,"meta":1990,"navigation":350,"path":2002,"seo":2003,"stem":2004,"__hash__":2005},"content\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions\u002Findex.md","Writing a Same-Document View Transition: the CSS You Actually Author",{"type":7,"value":8,"toc":1978},"minimark",[9,13,23,31,34,39,52,68,71,73,77,88,91,186,189,193,195,199,202,1550,1568,1570,1574,1577,1587,1599,1624,1626,1630,1635,1851,1861,1863,1867,1874,1884,1886,1890,1900,1913,1919,1932,1934,1938,1975],[10,11,5],"h1",{"id":12},"writing-a-same-document-view-transition-the-css-you-actually-author",[14,15,16,17,22],"p",{},"You have a panel that switches between a grid arrangement and a list arrangement, or a tab strip whose content is replaced on click. The change is one class toggle. Without help the swap is instantaneous and slightly disorienting — content jumps from one arrangement to another with no visual thread connecting them. A same-document view transition supplies that thread, and this page walks through exactly which CSS you write to control it. It assumes the model described in ",[18,19,21],"a",{"href":20},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002F","View Transitions for CSS Developers",": the browser takes a picture before, a picture after, and animates between them.",[14,24,25,26,30],{},"The narrow question here is not ",[27,28,29],"em",{},"what a view transition is",". It is: given a working transition, what are the smallest, most useful CSS declarations, in what order should you reach for them, and where does each one break?",[32,33],"hr",{},[35,36,38],"h2",{"id":37},"why-reach-for-this-instead-of-a-plain-transition","Why reach for this instead of a plain transition",[14,40,41,42,46,47,51],{},"A CSS ",[43,44,45],"code",{},"transition"," needs a living element whose property values change. That covers hover, focus, and toggles — the territory of ",[18,48,50],{"href":49},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002F","CSS Transition Fundamentals",". It does not cover a change where the old content is removed from the DOM and different content takes its place, because there is no continuous element to interpolate on.",[14,53,54,55,58,59,62,63,67],{},"You can fake continuity with ",[43,56,57],{},"@starting-style"," for elements arriving and ",[43,60,61],{},"transition-behavior: allow-discrete"," for elements leaving, and for a single panel that is often the right call — it is pure CSS, it needs no JavaScript entry point, and it does not freeze the page. But it falls apart when the change is structural: when a grid becomes a list, twenty elements each need their own enter and exit choreography, and none of them can visually travel from where the old layout put them to where the new layout puts them. That is precisely the gap a view transition fills, and it is why the two techniques coexist rather than compete. If your change is one element appearing or disappearing, prefer ",[18,64,66],{"href":65},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fstarting-style-entry-animations\u002F","@starting-style entry animations",". If it is a re-arrangement, use a view transition.",[14,69,70],{},"The accessibility tradeoff is real and worth stating: for the duration of a view transition the page is a picture. Nothing is clickable, nothing is selectable, and focus is wherever you left it. That is an argument for short durations and for never chaining transitions back to back.",[32,72],{},[35,74,76],{"id":75},"the-two-snapshots-share-one-box","The two snapshots share one box",[14,78,79,80,83,84,87],{},"Everything about authoring these animations follows from one fact: ",[43,81,82],{},"::view-transition-old()"," and ",[43,85,86],{},"::view-transition-new()"," are absolutely positioned siblings inside the same image pair, stacked on top of each other with the new one on top. The default animations are chosen so that stacking is invisible — as one fades out the other fades in, and at every instant the composite reads as a single image.",[14,89,90],{},"The moment you replace those animations you own that problem. If you give the new snapshot a slide-in and leave the old one opaque, the old one sits underneath the whole time and you get a doubled, ghosted image. Custom animations almost always come in pairs.",[92,93,99,100,99,104,99,108,99,115,99,121,99,125,99,137,99,140,99,149,99,154,99,158,99,162,99,167,99,170,99,173,99,175,99,178,99,181],"svg",{"viewBox":94,"role":95,"ariaLabel":96,"xmlns":97,"style":98},"0 0 720 340","img","Comparison of the default snapshot animations and custom replacements inside the shared image pair","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[101,102,103],"title",{},"Default versus custom snapshot animations",[105,106,107],"desc",{},"Two panels showing the old and new snapshots stacked in one box: on the left the user-agent opacity keyframes, on the right custom slide keyframes.",[109,110,114],"text",{"x":111,"y":112,"style":113},"360","24","text-anchor:middle;fill:currentColor;font:700 18px sans-serif","One box, two stacked snapshots",[109,116,120],{"x":117,"y":118,"style":119},"185","52","text-anchor:middle;fill:#7aa2ff;font:600 14px sans-serif","Default cross-fade",[109,122,124],{"x":123,"y":118,"style":119},"535","Your own keyframes",[126,127],"rect",{"x":128,"y":129,"width":130,"height":131,"rx":132,"fill":133,"stroke":134,"strokeWidth":135,"opacity":136},"30","62","310","228","12","none","currentColor","1.5","0.45",[126,138],{"x":139,"y":129,"width":130,"height":131,"rx":132,"fill":133,"stroke":134,"strokeWidth":135,"opacity":136},"380",[126,141],{"x":142,"y":143,"width":144,"height":145,"rx":146,"fill":147,"opacity":148},"60","100","250","48","8","#7aa2ff","0.16",[109,150,153],{"x":117,"y":151,"style":152},"130","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace","old: opacity 1 to 0",[126,155],{"x":142,"y":156,"width":144,"height":145,"rx":146,"fill":147,"opacity":157},"172","0.30",[109,159,161],{"x":117,"y":160,"style":152},"202","new: opacity 0 to 1",[109,163,166],{"x":117,"y":164,"style":165},"255","text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.75","user-agent keyframes, 250ms",[126,168],{"x":169,"y":143,"width":144,"height":145,"rx":146,"fill":147,"opacity":148},"410",[109,171,172],{"x":123,"y":151,"style":152},"old: fade + exit left",[126,174],{"x":169,"y":156,"width":144,"height":145,"rx":146,"fill":147,"opacity":157},[109,176,177],{"x":123,"y":160,"style":152},"new: fade + enter right",[109,179,180],{"x":123,"y":164,"style":165},"both must be declared together",[109,182,185],{"x":111,"y":183,"style":184},"320","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.7","Leave the old snapshot opaque and it ghosts underneath the new one.",[14,187,188],{},"The demo below runs those custom keyframes on ordinary elements, so you can judge the curve and the travel distance before wiring anything up.",[190,191],"live-demo",{"name":192},"vt-slide-old-new",[32,194],{},[35,196,198],{"id":197},"complete-working-implementation","Complete working implementation",[14,200,201],{},"A tabbed panel where selecting a tab replaces the panel body. The old body exits to the left, the new body enters from the right, and the surrounding chrome is left alone by giving the panel its own name so only it animates.",[203,204,209],"pre",{"className":205,"code":206,"language":207,"meta":208,"style":208},"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\u003Ctitle>Same-document view transition\u003C\u002Ftitle>\n\u003Cstyle>\n  body { font-family: system-ui, sans-serif; margin: 2rem; }\n\n  .tabs { display: flex; gap: .5rem; margin-bottom: 1rem; }\n  .tab {\n    padding: .5rem 1rem; border: 1px solid currentColor;\n    border-radius: 999px; background: none; cursor: pointer;\n  }\n  .tab[aria-selected=\"true\"] { background: currentColor; color: Canvas; }\n\n  .panel {\n    padding: 1.5rem;\n    border: 1px solid currentColor;\n    border-radius: 12px;\n    \u002F* Lifts the panel out of the root snapshot so only this box animates\n       and the tab strip above it stays perfectly still. *\u002F\n    view-transition-name: tab-panel;\n  }\n\n  \u002F* Give the overlay an opaque backdrop. Without it, the moment both\n     snapshots are partly transparent you see straight through to the\n     live DOM underneath, which reads as a flash. *\u002F\n  ::view-transition { background-color: Canvas; }\n\n  \u002F* Retime the group so the panel's size change tracks the content swap. *\u002F\n  ::view-transition-group(tab-panel) {\n    animation-duration: 300ms;\n    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  }\n\n  @keyframes vt-slide-out-left {\n    from { opacity: 1; transform: translateX(0); }\n    to   { opacity: 0; transform: translateX(-30%); }\n  }\n  @keyframes vt-slide-in-right {\n    from { opacity: 0; transform: translateX(30%); }\n    to   { opacity: 1; transform: translateX(0); }\n  }\n\n  \u002F* 'both' is load-bearing: without a fill mode the snapshot reverts to\n     its start value on the final frame and you get a one-frame flash. *\u002F\n  ::view-transition-old(tab-panel) {\n    animation: vt-slide-out-left 300ms cubic-bezier(0.4, 0, 0.2, 1) both;\n  }\n  ::view-transition-new(tab-panel) {\n    animation: vt-slide-in-right 300ms cubic-bezier(0.4, 0, 0.2, 1) both;\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n\n\u003Cdiv class=\"tabs\" role=\"tablist\">\n  \u003Cbutton class=\"tab\" role=\"tab\" aria-selected=\"true\"  data-body=\"Colour tokens live in one layer.\">Design\u003C\u002Fbutton>\n  \u003Cbutton class=\"tab\" role=\"tab\" aria-selected=\"false\" data-body=\"Every component ships its own styles.\">Code\u003C\u002Fbutton>\n  \u003Cbutton class=\"tab\" role=\"tab\" aria-selected=\"false\" data-body=\"Budgets are enforced in CI.\">Perf\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n\n\u003Cdiv class=\"panel\" id=\"panel\" role=\"tabpanel\" tabindex=\"0\">\n  \u003Cp>Colour tokens live in one layer.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\n\u003Cscript>\n  const panel = document.getElementById('panel');\n\n  function show(tab) {\n    document.querySelectorAll('.tab').forEach(t =>\n      t.setAttribute('aria-selected', String(t === tab)));\n    panel.innerHTML = '\u003Cp>' + tab.dataset.body + '\u003C\u002Fp>';\n  }\n\n  document.querySelectorAll('.tab').forEach(tab => {\n    tab.addEventListener('click', () => {\n      \u002F\u002F Feature test: unsupported engines get the content change instantly.\n      if (!document.startViewTransition) { show(tab); return; }\n      document.startViewTransition(() => show(tab));\n    });\n  });\n\u003C\u002Fscript>\n\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n","html","",[43,210,211,231,251,261,279,293,303,345,352,394,403,441,475,481,514,519,527,540,558,571,578,584,593,598,603,609,615,621,637,642,648,660,676,710,715,720,732,764,797,802,812,841,868,873,878,884,890,900,940,945,955,991,996,1006,1015,1025,1030,1056,1100,1141,1180,1189,1194,1232,1246,1255,1260,1270,1296,1301,1317,1345,1373,1398,1403,1408,1433,1455,1461,1487,1506,1512,1518,1527,1532,1541],{"__ignoreMap":208},[212,213,216,220,224,228],"span",{"class":214,"line":215},"line",1,[212,217,219],{"class":218},"suds8","\u003C!",[212,221,223],{"class":222},"sne4z","DOCTYPE",[212,225,227],{"class":226},"s-5SL"," html",[212,229,230],{"class":218},">\n",[212,232,234,237,239,242,245,249],{"class":214,"line":233},2,[212,235,236],{"class":218},"\u003C",[212,238,207],{"class":222},[212,240,241],{"class":226}," lang",[212,243,244],{"class":218},"=",[212,246,248],{"class":247},"sT6z2","\"en\"",[212,250,230],{"class":218},[212,252,254,256,259],{"class":214,"line":253},3,[212,255,236],{"class":218},[212,257,258],{"class":222},"head",[212,260,230],{"class":218},[212,262,264,266,269,272,274,277],{"class":214,"line":263},4,[212,265,236],{"class":218},[212,267,268],{"class":222},"meta",[212,270,271],{"class":226}," charset",[212,273,244],{"class":218},[212,275,276],{"class":247},"\"utf-8\"",[212,278,230],{"class":218},[212,280,282,284,286,289,291],{"class":214,"line":281},5,[212,283,236],{"class":218},[212,285,101],{"class":222},[212,287,288],{"class":218},">Same-document view transition\u003C\u002F",[212,290,101],{"class":222},[212,292,230],{"class":218},[212,294,296,298,301],{"class":214,"line":295},6,[212,297,236],{"class":218},[212,299,300],{"class":222},"style",[212,302,230],{"class":218},[212,304,306,309,312,315,318,321,324,327,330,333,335,338,342],{"class":214,"line":305},7,[212,307,308],{"class":222},"  body",[212,310,311],{"class":218}," { ",[212,313,314],{"class":226},"font-family",[212,316,317],{"class":218},": ",[212,319,320],{"class":226},"system-ui",[212,322,323],{"class":218},", ",[212,325,326],{"class":226},"sans-serif",[212,328,329],{"class":218},"; ",[212,331,332],{"class":226},"margin",[212,334,317],{"class":218},[212,336,337],{"class":226},"2",[212,339,341],{"class":340},"sHUrx","rem",[212,343,344],{"class":218},"; }\n",[212,346,348],{"class":214,"line":347},8,[212,349,351],{"emptyLinePlaceholder":350},true,"\n",[212,353,355,358,360,363,365,368,370,373,375,378,380,382,385,387,390,392],{"class":214,"line":354},9,[212,356,357],{"class":226},"  .tabs",[212,359,311],{"class":218},[212,361,362],{"class":226},"display",[212,364,317],{"class":218},[212,366,367],{"class":226},"flex",[212,369,329],{"class":218},[212,371,372],{"class":226},"gap",[212,374,317],{"class":218},[212,376,377],{"class":226},".5",[212,379,341],{"class":340},[212,381,329],{"class":218},[212,383,384],{"class":226},"margin-bottom",[212,386,317],{"class":218},[212,388,389],{"class":226},"1",[212,391,341],{"class":340},[212,393,344],{"class":218},[212,395,397,400],{"class":214,"line":396},10,[212,398,399],{"class":226},"  .tab",[212,401,402],{"class":218}," {\n",[212,404,406,409,411,413,415,418,420,422,425,427,429,432,435,438],{"class":214,"line":405},11,[212,407,408],{"class":226},"    padding",[212,410,317],{"class":218},[212,412,377],{"class":226},[212,414,341],{"class":340},[212,416,417],{"class":226}," 1",[212,419,341],{"class":340},[212,421,329],{"class":218},[212,423,424],{"class":226},"border",[212,426,317],{"class":218},[212,428,389],{"class":226},[212,430,431],{"class":340},"px",[212,433,434],{"class":226}," solid",[212,436,437],{"class":226}," currentColor",[212,439,440],{"class":218},";\n",[212,442,444,447,449,452,454,456,459,461,463,465,468,470,473],{"class":214,"line":443},12,[212,445,446],{"class":226},"    border-radius",[212,448,317],{"class":218},[212,450,451],{"class":226},"999",[212,453,431],{"class":340},[212,455,329],{"class":218},[212,457,458],{"class":226},"background",[212,460,317],{"class":218},[212,462,133],{"class":226},[212,464,329],{"class":218},[212,466,467],{"class":226},"cursor",[212,469,317],{"class":218},[212,471,472],{"class":226},"pointer",[212,474,440],{"class":218},[212,476,478],{"class":214,"line":477},13,[212,479,480],{"class":218},"  }\n",[212,482,484,486,489,492,494,497,500,502,504,506,508,511],{"class":214,"line":483},14,[212,485,399],{"class":226},[212,487,488],{"class":218},"[",[212,490,491],{"class":226},"aria-selected",[212,493,244],{"class":340},[212,495,496],{"class":247},"\"true\"",[212,498,499],{"class":218},"] { ",[212,501,458],{"class":226},[212,503,317],{"class":218},[212,505,134],{"class":226},[212,507,329],{"class":218},[212,509,510],{"class":226},"color",[212,512,513],{"class":218},": Canvas; }\n",[212,515,517],{"class":214,"line":516},15,[212,518,351],{"emptyLinePlaceholder":350},[212,520,522,525],{"class":214,"line":521},16,[212,523,524],{"class":226},"  .panel",[212,526,402],{"class":218},[212,528,530,532,534,536,538],{"class":214,"line":529},17,[212,531,408],{"class":226},[212,533,317],{"class":218},[212,535,135],{"class":226},[212,537,341],{"class":340},[212,539,440],{"class":218},[212,541,543,546,548,550,552,554,556],{"class":214,"line":542},18,[212,544,545],{"class":226},"    border",[212,547,317],{"class":218},[212,549,389],{"class":226},[212,551,431],{"class":340},[212,553,434],{"class":226},[212,555,437],{"class":226},[212,557,440],{"class":218},[212,559,561,563,565,567,569],{"class":214,"line":560},19,[212,562,446],{"class":226},[212,564,317],{"class":218},[212,566,132],{"class":226},[212,568,431],{"class":340},[212,570,440],{"class":218},[212,572,574],{"class":214,"line":573},20,[212,575,577],{"class":576},"sLBg1","    \u002F* Lifts the panel out of the root snapshot so only this box animates\n",[212,579,581],{"class":214,"line":580},21,[212,582,583],{"class":576},"       and the tab strip above it stays perfectly still. *\u002F\n",[212,585,587,590],{"class":214,"line":586},22,[212,588,589],{"class":226},"    view-transition-name",[212,591,592],{"class":218},": tab-panel;\n",[212,594,596],{"class":214,"line":595},23,[212,597,480],{"class":218},[212,599,601],{"class":214,"line":600},24,[212,602,351],{"emptyLinePlaceholder":350},[212,604,606],{"class":214,"line":605},25,[212,607,608],{"class":576},"  \u002F* Give the overlay an opaque backdrop. Without it, the moment both\n",[212,610,612],{"class":214,"line":611},26,[212,613,614],{"class":576},"     snapshots are partly transparent you see straight through to the\n",[212,616,618],{"class":214,"line":617},27,[212,619,620],{"class":576},"     live DOM underneath, which reads as a flash. *\u002F\n",[212,622,624,627,630,632,635],{"class":214,"line":623},28,[212,625,626],{"class":218},"  ::",[212,628,629],{"class":222},"view-transition",[212,631,311],{"class":218},[212,633,634],{"class":226},"background-color",[212,636,513],{"class":218},[212,638,640],{"class":214,"line":639},29,[212,641,351],{"emptyLinePlaceholder":350},[212,643,645],{"class":214,"line":644},30,[212,646,647],{"class":576},"  \u002F* Retime the group so the panel's size change tracks the content swap. *\u002F\n",[212,649,651,654,657],{"class":214,"line":650},31,[212,652,653],{"class":218},"  ::view-transition-group(",[212,655,656],{"class":222},"tab-panel",[212,658,659],{"class":218},") {\n",[212,661,663,666,668,671,674],{"class":214,"line":662},32,[212,664,665],{"class":226},"    animation-duration",[212,667,317],{"class":218},[212,669,670],{"class":226},"300",[212,672,673],{"class":340},"ms",[212,675,440],{"class":218},[212,677,679,682,684,687,690,693,695,698,700,703,705,707],{"class":214,"line":678},33,[212,680,681],{"class":226},"    animation-timing-function",[212,683,317],{"class":218},[212,685,686],{"class":226},"cubic-bezier",[212,688,689],{"class":218},"(",[212,691,692],{"class":226},"0.4",[212,694,323],{"class":218},[212,696,697],{"class":226},"0",[212,699,323],{"class":218},[212,701,702],{"class":226},"0.2",[212,704,323],{"class":218},[212,706,389],{"class":226},[212,708,709],{"class":218},");\n",[212,711,713],{"class":214,"line":712},34,[212,714,480],{"class":218},[212,716,718],{"class":214,"line":717},35,[212,719,351],{"emptyLinePlaceholder":350},[212,721,723,726,730],{"class":214,"line":722},36,[212,724,725],{"class":340},"  @keyframes",[212,727,729],{"class":728},"soyes"," vt-slide-out-left",[212,731,402],{"class":218},[212,733,735,738,740,743,745,747,749,752,754,757,759,761],{"class":214,"line":734},37,[212,736,737],{"class":226},"    from",[212,739,311],{"class":218},[212,741,742],{"class":226},"opacity",[212,744,317],{"class":218},[212,746,389],{"class":226},[212,748,329],{"class":218},[212,750,751],{"class":226},"transform",[212,753,317],{"class":218},[212,755,756],{"class":226},"translateX",[212,758,689],{"class":218},[212,760,697],{"class":226},[212,762,763],{"class":218},"); }\n",[212,765,767,770,773,775,777,779,781,783,785,787,789,792,795],{"class":214,"line":766},38,[212,768,769],{"class":226},"    to",[212,771,772],{"class":218},"   { ",[212,774,742],{"class":226},[212,776,317],{"class":218},[212,778,697],{"class":226},[212,780,329],{"class":218},[212,782,751],{"class":226},[212,784,317],{"class":218},[212,786,756],{"class":226},[212,788,689],{"class":218},[212,790,791],{"class":226},"-30",[212,793,794],{"class":340},"%",[212,796,763],{"class":218},[212,798,800],{"class":214,"line":799},39,[212,801,480],{"class":218},[212,803,805,807,810],{"class":214,"line":804},40,[212,806,725],{"class":340},[212,808,809],{"class":728}," vt-slide-in-right",[212,811,402],{"class":218},[212,813,815,817,819,821,823,825,827,829,831,833,835,837,839],{"class":214,"line":814},41,[212,816,737],{"class":226},[212,818,311],{"class":218},[212,820,742],{"class":226},[212,822,317],{"class":218},[212,824,697],{"class":226},[212,826,329],{"class":218},[212,828,751],{"class":226},[212,830,317],{"class":218},[212,832,756],{"class":226},[212,834,689],{"class":218},[212,836,128],{"class":226},[212,838,794],{"class":340},[212,840,763],{"class":218},[212,842,844,846,848,850,852,854,856,858,860,862,864,866],{"class":214,"line":843},42,[212,845,769],{"class":226},[212,847,772],{"class":218},[212,849,742],{"class":226},[212,851,317],{"class":218},[212,853,389],{"class":226},[212,855,329],{"class":218},[212,857,751],{"class":226},[212,859,317],{"class":218},[212,861,756],{"class":226},[212,863,689],{"class":218},[212,865,697],{"class":226},[212,867,763],{"class":218},[212,869,871],{"class":214,"line":870},43,[212,872,480],{"class":218},[212,874,876],{"class":214,"line":875},44,[212,877,351],{"emptyLinePlaceholder":350},[212,879,881],{"class":214,"line":880},45,[212,882,883],{"class":576},"  \u002F* 'both' is load-bearing: without a fill mode the snapshot reverts to\n",[212,885,887],{"class":214,"line":886},46,[212,888,889],{"class":576},"     its start value on the final frame and you get a one-frame flash. *\u002F\n",[212,891,893,896,898],{"class":214,"line":892},47,[212,894,895],{"class":218},"  ::view-transition-old(",[212,897,656],{"class":222},[212,899,659],{"class":218},[212,901,903,906,909,911,913,916,918,920,922,924,926,928,930,932,935,938],{"class":214,"line":902},48,[212,904,905],{"class":226},"    animation",[212,907,908],{"class":218},": vt-slide-out-left ",[212,910,670],{"class":226},[212,912,673],{"class":340},[212,914,915],{"class":226}," cubic-bezier",[212,917,689],{"class":218},[212,919,692],{"class":226},[212,921,323],{"class":218},[212,923,697],{"class":226},[212,925,323],{"class":218},[212,927,702],{"class":226},[212,929,323],{"class":218},[212,931,389],{"class":226},[212,933,934],{"class":218},") ",[212,936,937],{"class":226},"both",[212,939,440],{"class":218},[212,941,943],{"class":214,"line":942},49,[212,944,480],{"class":218},[212,946,948,951,953],{"class":214,"line":947},50,[212,949,950],{"class":218},"  ::view-transition-new(",[212,952,656],{"class":222},[212,954,659],{"class":218},[212,956,958,960,963,965,967,969,971,973,975,977,979,981,983,985,987,989],{"class":214,"line":957},51,[212,959,905],{"class":226},[212,961,962],{"class":218},": vt-slide-in-right ",[212,964,670],{"class":226},[212,966,673],{"class":340},[212,968,915],{"class":226},[212,970,689],{"class":218},[212,972,692],{"class":226},[212,974,323],{"class":218},[212,976,697],{"class":226},[212,978,323],{"class":218},[212,980,702],{"class":226},[212,982,323],{"class":218},[212,984,389],{"class":226},[212,986,934],{"class":218},[212,988,937],{"class":226},[212,990,440],{"class":218},[212,992,994],{"class":214,"line":993},52,[212,995,480],{"class":218},[212,997,999,1002,1004],{"class":214,"line":998},53,[212,1000,1001],{"class":218},"\u003C\u002F",[212,1003,300],{"class":222},[212,1005,230],{"class":218},[212,1007,1009,1011,1013],{"class":214,"line":1008},54,[212,1010,1001],{"class":218},[212,1012,258],{"class":222},[212,1014,230],{"class":218},[212,1016,1018,1020,1023],{"class":214,"line":1017},55,[212,1019,236],{"class":218},[212,1021,1022],{"class":222},"body",[212,1024,230],{"class":218},[212,1026,1028],{"class":214,"line":1027},56,[212,1029,351],{"emptyLinePlaceholder":350},[212,1031,1033,1035,1038,1041,1043,1046,1049,1051,1054],{"class":214,"line":1032},57,[212,1034,236],{"class":218},[212,1036,1037],{"class":222},"div",[212,1039,1040],{"class":226}," class",[212,1042,244],{"class":218},[212,1044,1045],{"class":247},"\"tabs\"",[212,1047,1048],{"class":226}," role",[212,1050,244],{"class":218},[212,1052,1053],{"class":247},"\"tablist\"",[212,1055,230],{"class":218},[212,1057,1059,1062,1065,1067,1069,1072,1074,1076,1078,1081,1083,1085,1088,1090,1093,1096,1098],{"class":214,"line":1058},58,[212,1060,1061],{"class":218},"  \u003C",[212,1063,1064],{"class":222},"button",[212,1066,1040],{"class":226},[212,1068,244],{"class":218},[212,1070,1071],{"class":247},"\"tab\"",[212,1073,1048],{"class":226},[212,1075,244],{"class":218},[212,1077,1071],{"class":247},[212,1079,1080],{"class":226}," aria-selected",[212,1082,244],{"class":218},[212,1084,496],{"class":247},[212,1086,1087],{"class":226},"  data-body",[212,1089,244],{"class":218},[212,1091,1092],{"class":247},"\"Colour tokens live in one layer.\"",[212,1094,1095],{"class":218},">Design\u003C\u002F",[212,1097,1064],{"class":222},[212,1099,230],{"class":218},[212,1101,1103,1105,1107,1109,1111,1113,1115,1117,1119,1121,1123,1126,1129,1131,1134,1137,1139],{"class":214,"line":1102},59,[212,1104,1061],{"class":218},[212,1106,1064],{"class":222},[212,1108,1040],{"class":226},[212,1110,244],{"class":218},[212,1112,1071],{"class":247},[212,1114,1048],{"class":226},[212,1116,244],{"class":218},[212,1118,1071],{"class":247},[212,1120,1080],{"class":226},[212,1122,244],{"class":218},[212,1124,1125],{"class":247},"\"false\"",[212,1127,1128],{"class":226}," data-body",[212,1130,244],{"class":218},[212,1132,1133],{"class":247},"\"Every component ships its own styles.\"",[212,1135,1136],{"class":218},">Code\u003C\u002F",[212,1138,1064],{"class":222},[212,1140,230],{"class":218},[212,1142,1144,1146,1148,1150,1152,1154,1156,1158,1160,1162,1164,1166,1168,1170,1173,1176,1178],{"class":214,"line":1143},60,[212,1145,1061],{"class":218},[212,1147,1064],{"class":222},[212,1149,1040],{"class":226},[212,1151,244],{"class":218},[212,1153,1071],{"class":247},[212,1155,1048],{"class":226},[212,1157,244],{"class":218},[212,1159,1071],{"class":247},[212,1161,1080],{"class":226},[212,1163,244],{"class":218},[212,1165,1125],{"class":247},[212,1167,1128],{"class":226},[212,1169,244],{"class":218},[212,1171,1172],{"class":247},"\"Budgets are enforced in CI.\"",[212,1174,1175],{"class":218},">Perf\u003C\u002F",[212,1177,1064],{"class":222},[212,1179,230],{"class":218},[212,1181,1183,1185,1187],{"class":214,"line":1182},61,[212,1184,1001],{"class":218},[212,1186,1037],{"class":222},[212,1188,230],{"class":218},[212,1190,1192],{"class":214,"line":1191},62,[212,1193,351],{"emptyLinePlaceholder":350},[212,1195,1197,1199,1201,1203,1205,1208,1211,1213,1215,1217,1219,1222,1225,1227,1230],{"class":214,"line":1196},63,[212,1198,236],{"class":218},[212,1200,1037],{"class":222},[212,1202,1040],{"class":226},[212,1204,244],{"class":218},[212,1206,1207],{"class":247},"\"panel\"",[212,1209,1210],{"class":226}," id",[212,1212,244],{"class":218},[212,1214,1207],{"class":247},[212,1216,1048],{"class":226},[212,1218,244],{"class":218},[212,1220,1221],{"class":247},"\"tabpanel\"",[212,1223,1224],{"class":226}," tabindex",[212,1226,244],{"class":218},[212,1228,1229],{"class":247},"\"0\"",[212,1231,230],{"class":218},[212,1233,1235,1237,1239,1242,1244],{"class":214,"line":1234},64,[212,1236,1061],{"class":218},[212,1238,14],{"class":222},[212,1240,1241],{"class":218},">Colour tokens live in one layer.\u003C\u002F",[212,1243,14],{"class":222},[212,1245,230],{"class":218},[212,1247,1249,1251,1253],{"class":214,"line":1248},65,[212,1250,1001],{"class":218},[212,1252,1037],{"class":222},[212,1254,230],{"class":218},[212,1256,1258],{"class":214,"line":1257},66,[212,1259,351],{"emptyLinePlaceholder":350},[212,1261,1263,1265,1268],{"class":214,"line":1262},67,[212,1264,236],{"class":218},[212,1266,1267],{"class":222},"script",[212,1269,230],{"class":218},[212,1271,1273,1276,1279,1282,1285,1289,1291,1294],{"class":214,"line":1272},68,[212,1274,1275],{"class":340},"  const",[212,1277,1278],{"class":226}," panel",[212,1280,1281],{"class":340}," =",[212,1283,1284],{"class":218}," document.",[212,1286,1288],{"class":1287},"sKwhi","getElementById",[212,1290,689],{"class":218},[212,1292,1293],{"class":247},"'panel'",[212,1295,709],{"class":218},[212,1297,1299],{"class":214,"line":1298},69,[212,1300,351],{"emptyLinePlaceholder":350},[212,1302,1304,1307,1310,1312,1315],{"class":214,"line":1303},70,[212,1305,1306],{"class":340},"  function",[212,1308,1309],{"class":1287}," show",[212,1311,689],{"class":218},[212,1313,1314],{"class":728},"tab",[212,1316,659],{"class":218},[212,1318,1320,1323,1326,1328,1331,1334,1337,1339,1342],{"class":214,"line":1319},71,[212,1321,1322],{"class":218},"    document.",[212,1324,1325],{"class":1287},"querySelectorAll",[212,1327,689],{"class":218},[212,1329,1330],{"class":247},"'.tab'",[212,1332,1333],{"class":218},").",[212,1335,1336],{"class":1287},"forEach",[212,1338,689],{"class":218},[212,1340,1341],{"class":728},"t",[212,1343,1344],{"class":340}," =>\n",[212,1346,1348,1351,1354,1356,1359,1361,1364,1367,1370],{"class":214,"line":1347},72,[212,1349,1350],{"class":218},"      t.",[212,1352,1353],{"class":1287},"setAttribute",[212,1355,689],{"class":218},[212,1357,1358],{"class":247},"'aria-selected'",[212,1360,323],{"class":218},[212,1362,1363],{"class":1287},"String",[212,1365,1366],{"class":218},"(t ",[212,1368,1369],{"class":340},"===",[212,1371,1372],{"class":218}," tab)));\n",[212,1374,1376,1379,1381,1384,1387,1390,1393,1396],{"class":214,"line":1375},73,[212,1377,1378],{"class":218},"    panel.innerHTML ",[212,1380,244],{"class":340},[212,1382,1383],{"class":247}," '\u003Cp>'",[212,1385,1386],{"class":340}," +",[212,1388,1389],{"class":218}," tab.dataset.body ",[212,1391,1392],{"class":340},"+",[212,1394,1395],{"class":247}," '\u003C\u002Fp>'",[212,1397,440],{"class":218},[212,1399,1401],{"class":214,"line":1400},74,[212,1402,480],{"class":218},[212,1404,1406],{"class":214,"line":1405},75,[212,1407,351],{"emptyLinePlaceholder":350},[212,1409,1411,1414,1416,1418,1420,1422,1424,1426,1428,1431],{"class":214,"line":1410},76,[212,1412,1413],{"class":218},"  document.",[212,1415,1325],{"class":1287},[212,1417,689],{"class":218},[212,1419,1330],{"class":247},[212,1421,1333],{"class":218},[212,1423,1336],{"class":1287},[212,1425,689],{"class":218},[212,1427,1314],{"class":728},[212,1429,1430],{"class":340}," =>",[212,1432,402],{"class":218},[212,1434,1436,1439,1442,1444,1447,1450,1453],{"class":214,"line":1435},77,[212,1437,1438],{"class":218},"    tab.",[212,1440,1441],{"class":1287},"addEventListener",[212,1443,689],{"class":218},[212,1445,1446],{"class":247},"'click'",[212,1448,1449],{"class":218},", () ",[212,1451,1452],{"class":340},"=>",[212,1454,402],{"class":218},[212,1456,1458],{"class":214,"line":1457},78,[212,1459,1460],{"class":576},"      \u002F\u002F Feature test: unsupported engines get the content change instantly.\n",[212,1462,1464,1467,1470,1473,1476,1479,1482,1485],{"class":214,"line":1463},79,[212,1465,1466],{"class":340},"      if",[212,1468,1469],{"class":218}," (",[212,1471,1472],{"class":340},"!",[212,1474,1475],{"class":218},"document.startViewTransition) { ",[212,1477,1478],{"class":1287},"show",[212,1480,1481],{"class":218},"(tab); ",[212,1483,1484],{"class":340},"return",[212,1486,344],{"class":218},[212,1488,1490,1493,1496,1499,1501,1503],{"class":214,"line":1489},80,[212,1491,1492],{"class":218},"      document.",[212,1494,1495],{"class":1287},"startViewTransition",[212,1497,1498],{"class":218},"(() ",[212,1500,1452],{"class":340},[212,1502,1309],{"class":1287},[212,1504,1505],{"class":218},"(tab));\n",[212,1507,1509],{"class":214,"line":1508},81,[212,1510,1511],{"class":218},"    });\n",[212,1513,1515],{"class":214,"line":1514},82,[212,1516,1517],{"class":218},"  });\n",[212,1519,1521,1523,1525],{"class":214,"line":1520},83,[212,1522,1001],{"class":218},[212,1524,1267],{"class":222},[212,1526,230],{"class":218},[212,1528,1530],{"class":214,"line":1529},84,[212,1531,351],{"emptyLinePlaceholder":350},[212,1533,1535,1537,1539],{"class":214,"line":1534},85,[212,1536,1001],{"class":218},[212,1538,1022],{"class":222},[212,1540,230],{"class":218},[212,1542,1544,1546,1548],{"class":214,"line":1543},86,[212,1545,1001],{"class":218},[212,1547,207],{"class":222},[212,1549,230],{"class":218},[14,1551,1552,1555,1556,1559,1560,1563,1564,1567],{},[43,1553,1554],{},"document.startViewTransition()"," is the only JavaScript entry point for a same-document transition — there is no CSS-only trigger, and no ",[43,1557,1558],{},":hover"," or ",[43,1561,1562],{},":checked"," state can start one. The callback should do nothing but mutate the DOM; every decision about how the change ",[27,1565,1566],{},"looks"," lives in the stylesheet above it.",[32,1569],{},[35,1571,1573],{"id":1572},"the-key-technique-pairing-the-exits-and-entrances","The key technique: pairing the exits and entrances",[14,1575,1576],{},"The single rule worth internalising is that a custom animation on one snapshot obliges you to write the matching one on the other. The user agent's defaults are a matched pair — a fade out and a fade in of identical duration — and their symmetry is what makes them invisible.",[14,1578,1579,1580,1582,1583,1586],{},"Two failure modes follow from breaking the pair. If you animate only ",[43,1581,86],{},", the old snapshot holds at full opacity beneath it and the incoming content appears to slide over a frozen copy of the outgoing content. If you give the two different durations, the transition runs for the length of the ",[27,1584,1585],{},"longer"," one, so a 150ms exit against a 400ms entrance leaves 250ms where the old snapshot has already gone and the new one is still arriving over an empty backdrop.",[14,1588,1589,1590,1593,1594,1598],{},"The fix for the first is simply to declare both. The fix for the second is either to match the durations or to overlap them intentionally with a delay — as in the card example where the entrance starts 60ms after the exit begins, producing a brief hand-off rather than a hard cut. That kind of offset timing is standard ",[43,1591,1592],{},"@keyframes"," craft; the patterns transfer directly from ",[18,1595,1597],{"href":1596},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002F","Keyframe Animation Patterns",".",[14,1600,1601,1602,1605,1606,1609,1610,1613,1614,1616,1617,1620,1621,1623],{},"One more detail belongs in this section: ",[43,1603,1604],{},"animation-fill-mode",". A snapshot with no fill mode reverts to its unanimated state on the frame after the animation ends, but the overlay is not removed until ",[27,1607,1608],{},"all"," animations in the group finish. If your old snapshot ends at ",[43,1611,1612],{},"opacity: 0"," without ",[43,1615,937],{},", it pops back to ",[43,1618,1619],{},"opacity: 1"," for the remaining frames of a longer group animation. Writing ",[43,1622,937],{}," in the shorthand costs four characters and removes a whole class of one-frame flashes.",[32,1625],{},[35,1627,1629],{"id":1628},"variation-direction-aware-transitions","Variation: direction-aware transitions",[14,1631,1632,1633,1598],{},"A left-to-right slide is wrong when the user goes backwards. Because the pseudo-element selectors are ordinary selectors, you can scope them under an attribute on the root element and set that attribute before calling ",[43,1634,1495],{},[203,1636,1640],{"className":1637,"code":1638,"language":1639,"meta":208,"style":208},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u002F* Forward is the default declared above. Backward mirrors it. *\u002F\nhtml[data-nav=\"back\"] ::view-transition-old(tab-panel) {\n  animation-name: vt-slide-out-right;\n}\nhtml[data-nav=\"back\"] ::view-transition-new(tab-panel) {\n  animation-name: vt-slide-in-left;\n}\n\n@keyframes vt-slide-out-right {\n  from { opacity: 1; transform: translateX(0); }\n  to   { opacity: 0; transform: translateX(30%); }\n}\n@keyframes vt-slide-in-left {\n  from { opacity: 0; transform: translateX(-30%); }\n  to   { opacity: 1; transform: translateX(0); }\n}\n","css",[43,1641,1642,1647,1668,1676,1681,1700,1707,1711,1715,1724,1751,1780,1784,1793,1821,1847],{"__ignoreMap":208},[212,1643,1644],{"class":214,"line":215},[212,1645,1646],{"class":576},"\u002F* Forward is the default declared above. Backward mirrors it. *\u002F\n",[212,1648,1649,1651,1653,1656,1658,1661,1664,1666],{"class":214,"line":233},[212,1650,207],{"class":222},[212,1652,488],{"class":218},[212,1654,1655],{"class":226},"data-nav",[212,1657,244],{"class":340},[212,1659,1660],{"class":247},"\"back\"",[212,1662,1663],{"class":218},"] ::view-transition-old(",[212,1665,656],{"class":222},[212,1667,659],{"class":218},[212,1669,1670,1673],{"class":214,"line":253},[212,1671,1672],{"class":226},"  animation-name",[212,1674,1675],{"class":218},": vt-slide-out-right;\n",[212,1677,1678],{"class":214,"line":263},[212,1679,1680],{"class":218},"}\n",[212,1682,1683,1685,1687,1689,1691,1693,1696,1698],{"class":214,"line":281},[212,1684,207],{"class":222},[212,1686,488],{"class":218},[212,1688,1655],{"class":226},[212,1690,244],{"class":340},[212,1692,1660],{"class":247},[212,1694,1695],{"class":218},"] ::view-transition-new(",[212,1697,656],{"class":222},[212,1699,659],{"class":218},[212,1701,1702,1704],{"class":214,"line":295},[212,1703,1672],{"class":226},[212,1705,1706],{"class":218},": vt-slide-in-left;\n",[212,1708,1709],{"class":214,"line":305},[212,1710,1680],{"class":218},[212,1712,1713],{"class":214,"line":347},[212,1714,351],{"emptyLinePlaceholder":350},[212,1716,1717,1719,1722],{"class":214,"line":354},[212,1718,1592],{"class":340},[212,1720,1721],{"class":728}," vt-slide-out-right",[212,1723,402],{"class":218},[212,1725,1726,1729,1731,1733,1735,1737,1739,1741,1743,1745,1747,1749],{"class":214,"line":396},[212,1727,1728],{"class":226},"  from",[212,1730,311],{"class":218},[212,1732,742],{"class":226},[212,1734,317],{"class":218},[212,1736,389],{"class":226},[212,1738,329],{"class":218},[212,1740,751],{"class":226},[212,1742,317],{"class":218},[212,1744,756],{"class":226},[212,1746,689],{"class":218},[212,1748,697],{"class":226},[212,1750,763],{"class":218},[212,1752,1753,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778],{"class":214,"line":405},[212,1754,1755],{"class":226},"  to",[212,1757,772],{"class":218},[212,1759,742],{"class":226},[212,1761,317],{"class":218},[212,1763,697],{"class":226},[212,1765,329],{"class":218},[212,1767,751],{"class":226},[212,1769,317],{"class":218},[212,1771,756],{"class":226},[212,1773,689],{"class":218},[212,1775,128],{"class":226},[212,1777,794],{"class":340},[212,1779,763],{"class":218},[212,1781,1782],{"class":214,"line":443},[212,1783,1680],{"class":218},[212,1785,1786,1788,1791],{"class":214,"line":477},[212,1787,1592],{"class":340},[212,1789,1790],{"class":728}," vt-slide-in-left",[212,1792,402],{"class":218},[212,1794,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819],{"class":214,"line":483},[212,1796,1728],{"class":226},[212,1798,311],{"class":218},[212,1800,742],{"class":226},[212,1802,317],{"class":218},[212,1804,697],{"class":226},[212,1806,329],{"class":218},[212,1808,751],{"class":226},[212,1810,317],{"class":218},[212,1812,756],{"class":226},[212,1814,689],{"class":218},[212,1816,791],{"class":226},[212,1818,794],{"class":340},[212,1820,763],{"class":218},[212,1822,1823,1825,1827,1829,1831,1833,1835,1837,1839,1841,1843,1845],{"class":214,"line":516},[212,1824,1755],{"class":226},[212,1826,772],{"class":218},[212,1828,742],{"class":226},[212,1830,317],{"class":218},[212,1832,389],{"class":226},[212,1834,329],{"class":218},[212,1836,751],{"class":226},[212,1838,317],{"class":218},[212,1840,756],{"class":226},[212,1842,689],{"class":218},[212,1844,697],{"class":226},[212,1846,763],{"class":218},[212,1848,1849],{"class":214,"line":521},[212,1850,1680],{"class":218},[14,1852,1853,1854,1857,1858,1860],{},"Set ",[43,1855,1856],{},"document.documentElement.dataset.nav = 'back'"," immediately before starting the transition and clear it afterwards. Because the snapshot styles are resolved when the overlay is built, the attribute only needs to be correct at that instant. The same hook is how you scope a transition to a theme, a locale, or a writing mode — for right-to-left interfaces, swap the sign on the ",[43,1859,756],{}," values or express them in logical terms with a custom property.",[32,1862],{},[35,1864,1866],{"id":1865},"browser-support-note","Browser support note",[14,1868,1869,1870,1873],{},"Same-document view transitions are available in Chrome and Edge from version 111, Safari from 18, and Firefox from 144 — every current engine supports them. The ",[43,1871,1872],{},"if (!document.startViewTransition)"," guard in the implementation above therefore protects older releases still in circulation rather than one hold-out engine, and what those users get is the plain DOM change with no animation. That remains a genuinely acceptable outcome, which is why the guard is written as a baseline experience rather than as a fallback path you have to design twice.",[14,1875,1876,1877,1880,1881,1883],{},"There is no ",[43,1878,1879],{},"@supports"," query that reliably tests for the pseudo-element tree, so do the detection in script. The CSS itself needs no guarding: an engine that does not know ",[43,1882,82],{}," discards those rule blocks as invalid selectors and never applies them, which is exactly the behaviour you want.",[32,1885],{},[35,1887,1889],{"id":1888},"faq","FAQ",[14,1891,1892,1896,1897,1899],{},[1893,1894,1895],"strong",{},"Can I start a same-document view transition without JavaScript?","\nNo. Inside a single document the only entry point is ",[43,1898,1554],{},", which takes a callback that performs the DOM change. The at-rule form only covers navigations between documents.",[14,1901,1902,1905,1906,1908,1909,1912],{},[1893,1903,1904],{},"Why does my custom animation snap back at the end?","\nThe snapshot returns to the animation's start value once it finishes because no fill mode is set. Add ",[43,1907,937],{}," to the ",[43,1910,1911],{},"animation"," shorthand so the final keyframe values are retained until the overlay is removed.",[14,1914,1915,1918],{},[1893,1916,1917],{},"What happens if the old and new animations have different durations?","\nThe transition lasts as long as the longest animation in the group. A short old animation leaves an empty gap where the outgoing snapshot has already vanished, so match the durations or overlap them deliberately with a delay.",[14,1920,1921,1924,1925,83,1928,1931],{},[1893,1922,1923],{},"Do I have to write animation-name to change the timing?","\nNo. Setting ",[43,1926,1927],{},"animation-duration",[43,1929,1930],{},"animation-timing-function"," on the old and new pseudo-elements keeps the user-agent keyframes and only retimes them, which is the smallest useful override.",[32,1933],{},[35,1935,1937],{"id":1936},"related","Related",[1939,1940,1941,1947,1954,1961,1968],"ul",{},[1942,1943,1944,1946],"li",{},[18,1945,21],{"href":20}," — the parent guide covering the snapshot model and the full pseudo-element tree.",[1942,1948,1949,1953],{},[18,1950,1952],{"href":1951},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-names-shared-elements\u002F","View transition names and shared elements"," — going beyond the root snapshot to morph one element into another.",[1942,1955,1956,1960],{},[18,1957,1959],{"href":1958},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion\u002F","View transitions with reduced motion"," — what to do with these slide keyframes when the user asks for less movement.",[1942,1962,1963,1967],{},[18,1964,1966],{"href":1965},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete\u002F","Transitioning display with allow-discrete"," — the pure-CSS alternative when only one element enters or leaves.",[1942,1969,1970,1974],{},[18,1971,1973],{"href":1972},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-sidebar-layouts\u002F","Container query sidebar layouts"," — the kind of arrangement change that benefits most from a transition.",[300,1976,1977],{},"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 .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}html pre.shiki code .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .sKwhi, html code.shiki .sKwhi{--shiki-default:#622CBC;--shiki-dark:#DBB7FF}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":208,"searchDepth":233,"depth":233,"links":1979},[1980,1981,1982,1983,1984,1985,1986,1987],{"id":37,"depth":233,"text":38},{"id":75,"depth":233,"text":76},{"id":197,"depth":233,"text":198},{"id":1572,"depth":233,"text":1573},{"id":1628,"depth":233,"text":1629},{"id":1865,"depth":233,"text":1866},{"id":1888,"depth":233,"text":1889},{"id":1936,"depth":233,"text":1937},"Animate a same-document view transition with CSS: what the default cross-fade gives you free and how to replace it with keyframes on the old and new snapshots.","md",{"pageTitle":1991,"datePublished":1992,"dateModified":1992,"faq":1993},"Same-Document View Transitions in CSS","2026-07-20",[1994,1996,1998,2000],{"q":1895,"a":1995},"No. Inside a single document the only entry point is document.startViewTransition(), which takes a callback that performs the DOM change. The at-rule form only covers navigations between documents.",{"q":1904,"a":1997},"The snapshot returns to the animation's start value once it finishes because no fill mode is set. Add both to the animation shorthand so the final keyframe values are retained until the overlay is removed.",{"q":1917,"a":1999},"The transition lasts as long as the longest animation in the group. A short old animation leaves an empty gap where the outgoing snapshot has already vanished, so match the durations or overlap them deliberately.",{"q":1923,"a":2001},"No. Setting animation-duration and animation-timing-function on the old and new pseudo-elements keeps the user-agent keyframes and only retimes them, which is the smallest useful override.","\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions",{"title":5,"description":1988},"css-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fsame-document-view-transitions\u002Findex","QZU5-DJcMMF6A_yYKYt3cRWkCQhI9-M6uYUuoZuU7hw",[2007,2010,2013,2016,2019,2022,2025,2028,2031,2034,2037,2040,2043,2046,2049,2052,2055,2058,2061,2064,2067,2070,2073,2076,2079,2082,2085,2088,2091,2094,2097,2100,2103,2106,2109,2112,2115,2118,2121,2124,2127,2130,2133,2136,2137,2140,2143,2146,2149,2152,2155,2158,2161,2164,2167,2170,2173,2176,2179,2182,2185,2188,2191,2194,2197,2200,2203,2206,2209,2212,2215,2218,2221,2224,2227,2230,2233,2236,2239,2242,2245,2248,2251,2254,2257,2260,2263,2266],{"path":2008,"title":2009},"\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":2011,"title":2012},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations","Accessibility in CSS Animations: Patterns, Specs & Best Practices",{"path":2014,"title":2015},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes","prefers-reduced-motion Recipes: A Reduction for Every Common Effect",{"path":2017,"title":2018},"\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":2020,"title":2021},"\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":2023,"title":2024},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns","Vestibular-Safe Animation Patterns: Why Some Motion Makes People Sick",{"path":2026,"title":2027},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fwcag-motion-success-criteria","WCAG Motion Success Criteria: What Each One Actually Requires",{"path":2029,"title":2030},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fcontainer-query-hover-affordances","Container Query Hover Affordances: When Narrow Means Persistent, Not Hidden",{"path":2032,"title":2033},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion","Container-Aware Motion: Animation Sized by the Space a Component Actually Has",{"path":2035,"title":2036},"\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":2038,"title":2039},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fsuppressing-motion-in-small-containers","Suppressing Motion in Small Containers: Deliberately Animating Nothing",{"path":2041,"title":2042},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api","CSS Animation vs the Web Animations API: A Decision Guide",{"path":2044,"title":2045},"\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":2047,"title":2048},"\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":2050,"title":2051},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations","Fluid Spacing Tokens That Drive Transition Durations",{"path":2053,"title":2054},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture","CSS Custom Properties Architecture: Scalable Design Systems & Dynamic UI",{"path":2056,"title":2057},"\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":2059,"title":2060},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions","CSS Transition Timing Functions: ease, cubic-bezier(), steps() and linear()",{"path":2062,"title":2063},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals","CSS Transition Fundamentals: Architecture, Performance & Patterns",{"path":2065,"title":2066},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fstarting-style-entry-animations","@starting-style: Animating an Element's Very First Style Change",{"path":2068,"title":2069},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete","Transitioning display with transition-behavior: allow-discrete and @starting-style",{"path":2071,"title":2072},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips","Accessible CSS-Only Tooltips and Hover Cards With  and ",{"path":2074,"title":2075},"\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":2077,"title":2078},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-within-form-patterns"," Form Patterns: Styling a Group From the Field Inside It",{"path":2080,"title":2081},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design","Hover & Focus State Design: Spec-Compliant Patterns for Modern UIs",{"path":2083,"title":2084},"\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":2086,"title":2087},"\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":2089,"title":2090},"\u002Fcss-only-micro-interactions-animations","CSS-Only Micro-Interactions & Animations: Architecture, Performance & Implementation",{"path":2092,"title":2093},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations","Container-Query-Triggered Keyframe Animations: Animate Differently per Available Space",{"path":2095,"title":2096},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-accordions-and-disclosure","CSS-Only Accordions and Disclosure Widgets with details and summary",{"path":2098,"title":2099},"\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":2101,"title":2102},"\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":2104,"title":2105},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns","Keyframe Animation Patterns: Spec-Compliant Architectures for Modern UIs",{"path":2107,"title":2108},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties","Staggered List Animations Using --i Index Custom Properties",{"path":2110,"title":2111},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration","Performance & GPU Acceleration in CSS: A Developer’s Blueprint",{"path":2113,"title":2114},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps","Optimizing CSS Animations for 60fps: Budgeting the Frame",{"path":2116,"title":2117},"\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":2119,"title":2120},"\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":2122,"title":2123},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations","Scroll-Driven Animations: Binding Motion to Scroll Position in Pure CSS",{"path":2125,"title":2126},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-animation-fallbacks","Shipping Scroll-Driven Animations Without Breaking Anything",{"path":2128,"title":2129},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-progress-bar-without-javascript","A Reading Progress Bar Driven by scroll(root)",{"path":2131,"title":2132},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-triggered-reveal-animations","Reveal-on-Scroll with view() and animation-range",{"path":2134,"title":2135},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers","View Transitions for CSS Developers: the Pseudo-Element Tree, Snapshots, and Named Elements",{"path":2002,"title":5},{"path":2138,"title":2139},"\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":2141,"title":2142},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion","Honouring prefers-reduced-motion in View Transitions",{"path":2144,"title":2145},"\u002F","Modern CSS Layouts & Micro-Interactions",{"path":2147,"title":2148},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioned-tooltips","Anchor-Positioned Tooltips: Tethering, Flipping, and Getting the Semantics Right",{"path":2150,"title":2151},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioning-fallbacks","Anchor Positioning Fallbacks: A Graceful Floor for Older Browser Versions",{"path":2153,"title":2154},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays","CSS Anchor Positioning and Overlays: Tethering Elements Without JavaScript",{"path":2156,"title":2157},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-attribute-and-css-styling","The popover Attribute: Top-Layer Overlays With No JavaScript",{"path":2159,"title":2160},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports","Feature Detection with @supports: Syntax, Semantics, and the Traps",{"path":2162,"title":2163},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers","Handling Container Query Fallbacks for Older Browsers",{"path":2165,"title":2166},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks","Container Query Fallbacks: Spec-Compliant CSS Strategies for Legacy Browsers",{"path":2168,"title":2169},"\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":2171,"title":2172},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-type-size-vs-inline-size","Choosing container-type: inline-size, size, or normal",{"path":2174,"title":2175},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison","Container vs Media Queries: Choosing the Right Reference Box",{"path":2177,"title":2178},"\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":2180,"title":2181},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics","Container Query Syntax Basics",{"path":2183,"title":2184},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries","Nesting and Naming Container Queries: Targeting the Right Ancestor",{"path":2186,"title":2187},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fauto-fit-minmax-responsive-grids","Responsive Grids with repeat(auto-fit, minmax())",{"path":2189,"title":2190},"\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":2192,"title":2193},"\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":2195,"title":2196},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts","CSS Grid and Subgrid Layouts for Responsive Interfaces",{"path":2198,"title":2199},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fsubgrid-vs-nested-grid","Subgrid vs an Independently Nested Grid",{"path":2201,"title":2202},"\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":2204,"title":2205},"\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":2207,"title":2208},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-type-accessibility-and-zoom","Fluid Type, Accessibility and Zoom: The vw-Only Trap",{"path":2210,"title":2211},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript","Fluid Typography Without JavaScript: A Precision CSS Reference",{"path":2213,"title":2214},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp","Fluid Typography with clamp(): A Practical Guide for Modern CSS",{"path":2216,"title":2217},"\u002Fmastering-container-queries-responsive-layouts","Mastering Container Queries & Responsive Layouts",{"path":2219,"title":2220},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media","Using aspect-ratio for Responsive Media and Preventing Layout Shift",{"path":2222,"title":2223},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques","Intrinsic Sizing Techniques: Modern CSS Layouts for Responsive UI",{"path":2225,"title":2226},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained","min-content, max-content, and fit-content() Explained",{"path":2228,"title":2229},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fpreventing-flex-and-grid-overflow","The min-width: auto Trap in Flex and Grid Layouts",{"path":2231,"title":2232},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens","Cascade Layers for Reset and Design Tokens",{"path":2234,"title":2235},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-vs-specificity-hacks","Cascade Layers vs Specificity Hacks: Ordering Instead of Escalating",{"path":2237,"title":2238},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies","Modern CSS Reset Strategies: A Spec-Compliant Foundation",{"path":2240,"title":2241},"\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":2243,"title":2244},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables","Container Query Data Tables: Reflowing Tables to Cards",{"path":2246,"title":2247},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-sidebar-layouts","Container Query Sidebar Layouts That Travel Between Contexts",{"path":2249,"title":2250},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns","Responsive Component Patterns: Architecture & Implementation",{"path":2252,"title":2253},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-forms-with-container-queries","Responsive Forms with Container Queries: Reflow Without Losing the Wiring",{"path":2255,"title":2256},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries","Responsive Navigation Without Media Queries Using Container Queries",{"path":2258,"title":2259},"\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":2261,"title":2262},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state","Style Queries and Container State: Token-Driven Component Variants",{"path":2264,"title":2265},"\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":2267,"title":2268},"\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",1784566157438]