[{"data":1,"prerenderedAt":1490},["ShallowReactive",2],{"page-\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget\u002F":3,"content-all-pages":925,"live-demo:keyboard-chat-layout":1484},{"id":4,"title":5,"body":6,"description":908,"extension":909,"meta":910,"navigation":286,"path":921,"seo":922,"stem":923,"__hash__":924},"content\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget\u002Findex.md","Layouts That Survive the Virtual Keyboard",{"type":7,"value":8,"toc":896},"minimark",[9,13,33,38,54,77,177,181,184,188,235,594,605,609,615,676,680,712,716,732,736,747,782,788,792,795,799,827,831,837,846,855,861,865,892],[10,11,5],"h1",{"id":12},"layouts-that-survive-the-virtual-keyboard",[14,15,16,17,21,22,27,28,32],"p",{},"A chat screen looks perfect in a mobile browser until someone taps the message field. The on-screen keyboard slides up, and depending on the browser the input bar disappears behind it, the header scrolls off the top, or the whole page shifts in a way the layout never expected. Desktop testing never reveals this, and even mobile emulators in DevTools do not show a keyboard. The behaviour comes down to which viewport the keyboard shrinks, and CSS now has tools to choose that behaviour and react to it: the ",[18,19,20],"code",{},"interactive-widget"," viewport setting, dynamic viewport units, and keyboard environment variables. This page covers all three with fallbacks. It belongs to ",[23,24,26],"a",{"href":25},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002F","Viewport Units & Mobile Layout"," in the ",[23,29,31],{"href":30},"\u002Fmastering-container-queries-responsive-layouts\u002F","Mastering Container Queries & Responsive Layouts"," guide.",[34,35,37],"h2",{"id":36},"two-viewports-and-a-keyboard","Two viewports and a keyboard",[14,39,40,41,45,46,49,50,53],{},"Mobile browsers track two viewports. The ",[42,43,44],"em",{},"layout viewport"," is what CSS lays out against: ",[18,47,48],{},"position: fixed",", viewport units and percentage heights on the root all refer to it. The ",[42,51,52],{},"visual viewport"," is the part the user can actually see, which shrinks when they pinch-zoom or when overlays such as the keyboard appear. When the keyboard opens, browsers choose one of three behaviours:",[55,56,57,65,71],"ul",{},[58,59,60,64],"li",{},[61,62,63],"strong",{},"resizes-visual"," — only the visual viewport shrinks. The layout stays the same size, the keyboard covers its bottom part, and the browser scrolls to keep the focused input visible. This is the common default.",[58,66,67,70],{},[61,68,69],{},"resizes-content"," — the layout viewport shrinks too. The page re-lays out into the space above the keyboard, and fixed-bottom elements sit on top of the keyboard.",[58,72,73,76],{},[61,74,75],{},"overlays-content"," — neither viewport shrinks. The keyboard covers the page and the author handles it, typically with the keyboard environment variables.",[78,79,85,86,85,90,85,94,85,102,85,113,85,116,85,119,85,127,85,130,85,133,85,139,85,141,85,143,85,149,85,151,85,156,85,159,85,162,85,166,85,169,85,173,85,175],"svg",{"viewBox":80,"role":81,"ariaLabel":82,"xmlns":83,"style":84},"0 0 720 320","img","Three phones with the keyboard open: with resizes-visual the bottom bar is hidden under the keyboard; with resizes-content the layout shrinks so the bar sits above the keyboard; with overlays-content the keyboard covers the page and the author must reposition the bar","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[87,88,89],"title",{},"Where the input bar ends up",[91,92,93],"desc",{},"Three phone outlines. Each has a header at the top, a keyboard at the bottom, and a message input bar. Resizes-visual: the input bar is behind the keyboard and the page scrolls to reveal the focused field. Resizes-content: the whole layout fits above the keyboard, with the bar directly on top of it. Overlays-content: the keyboard sits over the page; the bar is covered unless moved up by the keyboard inset height.",[95,96,101],"text",{"textAnchor":97,"x":98,"y":99,"style":100},"middle","360","26","text-anchor:middle;fill:currentColor;font:700 17px sans-serif","Three keyboard behaviours",[103,104],"rect",{"x":105,"y":106,"width":107,"height":108,"rx":109,"fill":110,"stroke":111,"strokeWidth":112},"60","46","160","240","16","none","currentColor","2",[103,114],{"x":115,"y":106,"width":107,"height":108,"rx":109,"fill":110,"stroke":111,"strokeWidth":112},"280",[103,117],{"x":118,"y":106,"width":107,"height":108,"rx":109,"fill":110,"stroke":111,"strokeWidth":112},"500",[103,120],{"x":121,"y":122,"width":123,"height":124,"rx":125,"fill":111,"opacity":126},"70","56","140","22","4","0.15",[103,128],{"x":129,"y":122,"width":123,"height":124,"rx":125,"fill":111,"opacity":126},"290",[103,131],{"x":132,"y":122,"width":123,"height":124,"rx":125,"fill":111,"opacity":126},"510",[103,134],{"x":121,"y":135,"width":123,"height":136,"rx":137,"fill":111,"opacity":138},"186","90","6","0.3",[103,140],{"x":129,"y":135,"width":123,"height":136,"rx":137,"fill":111,"opacity":138},[103,142],{"x":132,"y":135,"width":123,"height":136,"rx":137,"fill":111,"opacity":138},[103,144],{"x":129,"y":145,"width":123,"height":146,"rx":125,"fill":147,"opacity":148},"156","24","#7aa2ff","0.7",[103,150],{"x":132,"y":145,"width":123,"height":146,"rx":125,"fill":147,"opacity":148},[95,152,155],{"textAnchor":97,"x":123,"y":153,"style":154},"236","text-anchor:middle;fill:currentColor;font:11px sans-serif","keyboard covers bar",[95,157,158],{"textAnchor":97,"x":98,"y":153,"style":154},"keyboard",[95,160,158],{"textAnchor":97,"x":161,"y":153,"style":154},"580",[95,163,165],{"textAnchor":97,"x":98,"y":164,"style":154},"172","input bar",[95,167,168],{"textAnchor":97,"x":161,"y":164,"style":154},"moved by env()",[95,170,63],{"textAnchor":97,"x":123,"y":171,"style":172},"306","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace",[95,174,69],{"textAnchor":97,"x":98,"y":171,"style":172},[95,176,75],{"textAnchor":97,"x":161,"y":171,"style":172},[34,178,180],{"id":179},"the-complete-implementation","The complete implementation",[14,182,183],{},"A chat layout that uses a three-row grid sized to the dynamic viewport height, opts in to layout resizing where supported, and falls back gracefully everywhere else. The demo shows the layout itself; the keyboard behaviour can only be tested on a real device.",[185,186],"live-demo",{"name":187},"keyboard-chat-layout",[189,190,195],"pre",{"className":191,"code":192,"language":193,"meta":194,"style":194},"language-html shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003Cmeta name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, interactive-widget=resizes-content\">\n","html","",[18,196,197,221],{"__ignoreMap":194},[198,199,202,206,210,214,217],"span",{"class":200,"line":201},"line",1,[198,203,205],{"class":204},"suds8","\u003C",[198,207,209],{"class":208},"sne4z","meta",[198,211,213],{"class":212},"s-5SL"," name",[198,215,216],{"class":204},"=",[198,218,220],{"class":219},"sT6z2","\"viewport\"\n",[198,222,224,227,229,232],{"class":200,"line":223},2,[198,225,226],{"class":212},"      content",[198,228,216],{"class":204},[198,230,231],{"class":219},"\"width=device-width, initial-scale=1, interactive-widget=resizes-content\"",[198,233,234],{"class":204},">\n",[189,236,240],{"className":237,"code":238,"language":239,"meta":194,"style":194},"language-css shiki shiki-themes github-light-high-contrast github-dark-high-contrast","html, body { margin: 0; block-size: 100%; }\n\n.chat {\n  display: grid;\n  grid-template-rows: auto 1fr auto;\n  block-size: 100vh;      \u002F* fallback for browsers without dvh *\u002F\n  block-size: 100dvh;     \u002F* follows toolbars, and the keyboard when content resizes *\u002F\n}\n\n.chat__header { padding: 0.75rem 1rem; border-block-end: 1px solid #e2e8f0; }\n\n.chat__messages {\n  overflow-y: auto;\n  overscroll-behavior: contain;   \u002F* do not scroll the page behind the thread *\u002F\n  padding: 1rem;\n}\n\n.chat__composer {\n  display: flex;\n  gap: 0.5rem;\n  padding: 0.75rem 1rem;\n  padding-block-end: max(0.75rem, env(safe-area-inset-bottom));\n  border-block-start: 1px solid #e2e8f0;\n}\n","css",[18,241,242,282,288,297,311,333,353,371,377,382,426,431,439,451,468,482,487,492,500,512,527,544,571,589],{"__ignoreMap":194},[198,243,244,246,249,252,255,258,261,264,267,270,272,275,279],{"class":200,"line":201},[198,245,193],{"class":208},[198,247,248],{"class":204},", ",[198,250,251],{"class":208},"body",[198,253,254],{"class":204}," { ",[198,256,257],{"class":212},"margin",[198,259,260],{"class":204},": ",[198,262,263],{"class":212},"0",[198,265,266],{"class":204},"; ",[198,268,269],{"class":212},"block-size",[198,271,260],{"class":204},[198,273,274],{"class":212},"100",[198,276,278],{"class":277},"sHUrx","%",[198,280,281],{"class":204},"; }\n",[198,283,284],{"class":200,"line":223},[198,285,287],{"emptyLinePlaceholder":286},true,"\n",[198,289,291,294],{"class":200,"line":290},3,[198,292,293],{"class":212},".chat",[198,295,296],{"class":204}," {\n",[198,298,300,303,305,308],{"class":200,"line":299},4,[198,301,302],{"class":212},"  display",[198,304,260],{"class":204},[198,306,307],{"class":212},"grid",[198,309,310],{"class":204},";\n",[198,312,314,317,319,322,325,328,331],{"class":200,"line":313},5,[198,315,316],{"class":212},"  grid-template-rows",[198,318,260],{"class":204},[198,320,321],{"class":212},"auto",[198,323,324],{"class":212}," 1",[198,326,327],{"class":277},"fr",[198,329,330],{"class":212}," auto",[198,332,310],{"class":204},[198,334,336,339,341,343,346,349],{"class":200,"line":335},6,[198,337,338],{"class":212},"  block-size",[198,340,260],{"class":204},[198,342,274],{"class":212},[198,344,345],{"class":277},"vh",[198,347,348],{"class":204},";      ",[198,350,352],{"class":351},"sLBg1","\u002F* fallback for browsers without dvh *\u002F\n",[198,354,356,358,360,362,365,368],{"class":200,"line":355},7,[198,357,338],{"class":212},[198,359,260],{"class":204},[198,361,274],{"class":212},[198,363,364],{"class":277},"dvh",[198,366,367],{"class":204},";     ",[198,369,370],{"class":351},"\u002F* follows toolbars, and the keyboard when content resizes *\u002F\n",[198,372,374],{"class":200,"line":373},8,[198,375,376],{"class":204},"}\n",[198,378,380],{"class":200,"line":379},9,[198,381,287],{"emptyLinePlaceholder":286},[198,383,385,388,390,393,395,398,401,403,405,407,410,412,415,418,421,424],{"class":200,"line":384},10,[198,386,387],{"class":212},".chat__header",[198,389,254],{"class":204},[198,391,392],{"class":212},"padding",[198,394,260],{"class":204},[198,396,397],{"class":212},"0.75",[198,399,400],{"class":277},"rem",[198,402,324],{"class":212},[198,404,400],{"class":277},[198,406,266],{"class":204},[198,408,409],{"class":212},"border-block-end",[198,411,260],{"class":204},[198,413,414],{"class":212},"1",[198,416,417],{"class":277},"px",[198,419,420],{"class":212}," solid",[198,422,423],{"class":212}," #e2e8f0",[198,425,281],{"class":204},[198,427,429],{"class":200,"line":428},11,[198,430,287],{"emptyLinePlaceholder":286},[198,432,434,437],{"class":200,"line":433},12,[198,435,436],{"class":212},".chat__messages",[198,438,296],{"class":204},[198,440,442,445,447,449],{"class":200,"line":441},13,[198,443,444],{"class":212},"  overflow-y",[198,446,260],{"class":204},[198,448,321],{"class":212},[198,450,310],{"class":204},[198,452,454,457,459,462,465],{"class":200,"line":453},14,[198,455,456],{"class":212},"  overscroll-behavior",[198,458,260],{"class":204},[198,460,461],{"class":212},"contain",[198,463,464],{"class":204},";   ",[198,466,467],{"class":351},"\u002F* do not scroll the page behind the thread *\u002F\n",[198,469,471,474,476,478,480],{"class":200,"line":470},15,[198,472,473],{"class":212},"  padding",[198,475,260],{"class":204},[198,477,414],{"class":212},[198,479,400],{"class":277},[198,481,310],{"class":204},[198,483,485],{"class":200,"line":484},16,[198,486,376],{"class":204},[198,488,490],{"class":200,"line":489},17,[198,491,287],{"emptyLinePlaceholder":286},[198,493,495,498],{"class":200,"line":494},18,[198,496,497],{"class":212},".chat__composer",[198,499,296],{"class":204},[198,501,503,505,507,510],{"class":200,"line":502},19,[198,504,302],{"class":212},[198,506,260],{"class":204},[198,508,509],{"class":212},"flex",[198,511,310],{"class":204},[198,513,515,518,520,523,525],{"class":200,"line":514},20,[198,516,517],{"class":212},"  gap",[198,519,260],{"class":204},[198,521,522],{"class":212},"0.5",[198,524,400],{"class":277},[198,526,310],{"class":204},[198,528,530,532,534,536,538,540,542],{"class":200,"line":529},21,[198,531,473],{"class":212},[198,533,260],{"class":204},[198,535,397],{"class":212},[198,537,400],{"class":277},[198,539,324],{"class":212},[198,541,400],{"class":277},[198,543,310],{"class":204},[198,545,547,550,552,555,558,560,562,564,568],{"class":200,"line":546},22,[198,548,549],{"class":212},"  padding-block-end",[198,551,260],{"class":204},[198,553,554],{"class":212},"max",[198,556,557],{"class":204},"(",[198,559,397],{"class":212},[198,561,400],{"class":277},[198,563,248],{"class":204},[198,565,567],{"class":566},"soyes","env(safe-area-inset-bottom",[198,569,570],{"class":204},"));\n",[198,572,574,577,579,581,583,585,587],{"class":200,"line":573},23,[198,575,576],{"class":212},"  border-block-start",[198,578,260],{"class":204},[198,580,414],{"class":212},[198,582,417],{"class":277},[198,584,420],{"class":212},[198,586,423],{"class":212},[198,588,310],{"class":204},[198,590,592],{"class":200,"line":591},24,[198,593,376],{"class":204},[14,595,596,597,600,601,604],{},"With ",[18,598,599],{},"interactive-widget=resizes-content",", supporting browsers shrink the layout viewport when the keyboard opens. ",[18,602,603],{},"100dvh"," follows, the grid re-lays out into the space above the keyboard, and the composer row sits directly on top of it — the same result a native messaging app gives. The message list scrolls inside its own row, so the header stays visible. In browsers that ignore the meta value, the layout still behaves sensibly: the browser pans the visual viewport to reveal the focused field, the composer comes into view with it, and closing the keyboard returns everything to where it was. The page never depends on the enhancement to remain usable, which is the property that matters most for a layout this central.",[34,606,608],{"id":607},"the-key-technique-size-to-the-viewport-scroll-inside","The key technique: size to the viewport, scroll inside",[14,610,611,612,614],{},"The layout works in every keyboard mode because it avoids ",[18,613,48],{}," for the composer. Fixed elements are positioned against the layout viewport; in the common resizes-visual mode that viewport does not shrink, so a fixed composer ends up under the keyboard. A grid row, by contrast, is part of the document: the browser's own \"scroll the focused input into view\" behaviour brings it up above the keyboard even in resizes-visual mode, because the input is inside the composer.",[78,616,85,619,85,622,85,625,85,628,85,635,85,639,85,645,85,650,85,654,85,659,85,663,85,666,85,669,85,672],{"viewBox":617,"role":81,"ariaLabel":618,"xmlns":83,"style":84},"0 0 720 250","Comparison: a fixed-position composer stays at the bottom of the unchanged layout viewport, behind the keyboard; a grid-row composer is scrolled into view with its focused input",[87,620,621],{},"Fixed positioning versus a grid row",[91,623,624],{},"Left: position fixed bottom 0 attaches the composer to the layout viewport, which does not shrink in the default mode, so it stays behind the keyboard. Right: the composer is the last row of a 100dvh grid; when its input is focused, the browser scrolls it into view above the keyboard, and with resizes-content the grid simply fits above the keyboard.",[95,626,627],{"textAnchor":97,"x":98,"y":99,"style":100},"Why the composer is a grid row",[103,629],{"x":630,"y":631,"width":632,"height":107,"rx":633,"fill":111,"opacity":634},"40","50","300","10","0.08",[103,636],{"x":637,"y":631,"width":632,"height":107,"rx":633,"fill":147,"opacity":638},"380","0.2",[95,640,644],{"textAnchor":97,"x":641,"y":642,"style":643},"190","80","text-anchor:middle;fill:currentColor;font:600 13px ui-monospace,monospace","position: fixed; bottom: 0",[95,646,649],{"textAnchor":97,"x":641,"y":647,"style":648},"112","text-anchor:middle;fill:currentColor;font:12px sans-serif","attached to the layout viewport",[95,651,653],{"textAnchor":97,"x":641,"y":652,"style":648},"136","which does not shrink by default",[95,655,658],{"textAnchor":97,"x":641,"y":656,"style":657},"170","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","ends up behind the keyboard",[95,660,662],{"textAnchor":97,"x":661,"y":642,"style":643},"530","last row of a 100dvh grid",[95,664,665],{"textAnchor":97,"x":661,"y":647,"style":648},"focused input scrolled into view",[95,667,668],{"textAnchor":97,"x":661,"y":652,"style":648},"or fits above with resizes-content",[95,670,671],{"textAnchor":97,"x":661,"y":656,"style":657},"stays reachable in every mode",[95,673,675],{"textAnchor":97,"x":98,"y":674,"style":648},"238","Prefer document flow for anything that must stay above the keyboard.",[34,677,679],{"id":678},"why-100vh-is-the-wrong-height-here","Why 100vh is the wrong height here",[14,681,682,683,686,687,690,691,693,694,696,697,699,700,702,703,706,707,711],{},"On mobile, ",[18,684,685],{},"100vh"," resolves to the ",[42,688,689],{},"large"," viewport height — the height with browser toolbars retracted. When the page first loads with the address bar visible, a ",[18,692,685],{}," layout is taller than the screen, and its last row, the composer, sits below the visible area before the keyboard is even involved. ",[18,695,603],{}," tracks the toolbars as they show and hide, so the composer starts on screen. The implementation declares ",[18,698,685],{}," first as a fallback for browsers without dynamic units and overrides it with ",[18,701,603],{},"; browsers that understand the second declaration use it, and the rest ignore it. ",[18,704,705],{},"100svh"," is the alternative when the layout should never resize as toolbars move — it always uses the small viewport, leaving a gap when toolbars retract but never jumping. ",[23,708,710],{"href":709},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fdvh-svh-lvh-mobile-viewport-units\u002F","dvh, svh and lvh"," compares the three in detail.",[34,713,715],{"id":714},"keeping-the-focused-field-clear-of-other-chrome","Keeping the focused field clear of other chrome",[14,717,718,719,722,723,726,727,731],{},"Even when the browser scrolls a focused input into view, it only guarantees that the input itself is visible, not the send button next to it or a hint below it. Two small additions help. ",[18,720,721],{},"scroll-margin-block-end"," on the composer's input asks the browser to leave extra room below the field when bringing it into view, so a helper line or button row underneath stays visible. And for forms with a sticky header, ",[18,724,725],{},"scroll-padding-block-start"," on the scrolling container stops focused fields from landing underneath the header. ",[23,728,730],{"href":729},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fscroll-margin-and-scroll-padding-for-sticky-headers\u002F","scroll-margin and scroll-padding for Sticky Headers"," explains both properties.",[34,733,735],{"id":734},"opting-into-overlays-the-keyboard-environment-variables","Opting into overlays: the keyboard environment variables",[14,737,738,739,742,743,746],{},"Chromium-based browsers offer a third route through the VirtualKeyboard API. Script opts in with ",[18,740,741],{},"navigator.virtualKeyboard.overlaysContent = true",", after which the keyboard overlays the page without resizing anything, and CSS receives the keyboard's geometry through environment variables such as ",[18,744,745],{},"env(keyboard-inset-height)",":",[189,748,750],{"className":237,"code":749,"language":239,"meta":194,"style":194},".chat__composer {\n  \u002F* 0px when the keyboard is closed or the API is unavailable. *\u002F\n  margin-block-end: env(keyboard-inset-height, 0px);\n}\n",[18,751,752,758,763,778],{"__ignoreMap":194},[198,753,754,756],{"class":200,"line":201},[198,755,497],{"class":212},[198,757,296],{"class":204},[198,759,760],{"class":200,"line":223},[198,761,762],{"class":351},"  \u002F* 0px when the keyboard is closed or the API is unavailable. *\u002F\n",[198,764,765,768,771,773,775],{"class":200,"line":290},[198,766,767],{"class":212},"  margin-block-end",[198,769,770],{"class":204},": env(keyboard-inset-height, ",[198,772,263],{"class":212},[198,774,417],{"class":277},[198,776,777],{"class":204},");\n",[198,779,780],{"class":200,"line":299},[198,781,376],{"class":204},[14,783,784,785,787],{},"This gives complete control — the composer can animate up with the keyboard, for instance — but it only works where the API exists, and the page must handle everything the browser would otherwise have done. For most layouts, ",[18,786,69],{}," plus a grid layout is simpler and degrades better. Treat the API as a progressive enhancement for apps that need precise keyboard-aware positioning.",[34,789,791],{"id":790},"testing-on-real-devices","Testing on real devices",[14,793,794],{},"Desktop DevTools device emulation does not simulate the on-screen keyboard, so keyboard behaviour must be checked on real phones or a platform emulator that renders a keyboard. Check three things on each target browser: the focused input is visible while typing, the header or any other context stays where the design expects, and closing the keyboard restores the layout without leaving a gap. Test in both portrait and landscape; in landscape the keyboard can cover most of the screen, and a layout that needs more than the remaining height will scroll regardless of the techniques above. Remote debugging lets you inspect the live layout on the phone while the keyboard is open, which makes it easy to see which viewport has changed size.",[34,796,798],{"id":797},"browser-support","Browser support",[14,800,801,248,803,806,807,810,811,813,814,817,818,821,822,826],{},[18,802,364],{},[18,804,805],{},"svh"," and ",[18,808,809],{},"lvh"," units are supported in Chrome and Edge 108+, Firefox 101+ and Safari 15.4+. The ",[18,812,20],{}," viewport meta value and the VirtualKeyboard API with its ",[18,815,816],{},"keyboard-inset-*"," environment variables are not in the verified support table this site uses; at the time of writing they are available in Chromium-based browsers, with other engines behaving according to their own defaults. Because unsupported values are ignored, the grid-based layout above works everywhere and improves where they are supported. ",[18,819,820],{},"safe-area-inset-*"," values are covered in ",[23,823,825],{"href":824},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fsafe-area-insets-for-notched-screens\u002F","Safe Area Insets for Notched Screens",".",[34,828,830],{"id":829},"faq","FAQ",[14,832,833,836],{},[61,834,835],{},"Why does my fixed bottom bar hide behind the mobile keyboard?","\nBy default most mobile browsers overlay the keyboard on the page or resize only the visual viewport, leaving the layout viewport, and anything fixed to its bottom, where it was. The bar stays positioned against a viewport that is now partly covered.",[14,838,839,842,843,845],{},[61,840,841],{},"What does interactive-widget=resizes-content do?","\nIt is a value in the viewport meta tag that asks the browser to shrink the layout viewport when the on-screen keyboard opens. Fixed elements and ",[18,844,364],{},"-based heights then adapt to the space above the keyboard. Browser support varies, so treat it as an enhancement.",[14,847,848,851,852,854],{},[61,849,850],{},"Do dvh units account for the keyboard?","\nOnly when the browser resizes the layout viewport for the keyboard, for example with ",[18,853,599],{},". By default, dynamic viewport units track browser toolbars appearing and disappearing, not the keyboard.",[14,856,857,860],{},[61,858,859],{},"What is env(keyboard-inset-height)?","\nAn environment variable exposed by the VirtualKeyboard API in Chromium-based browsers when a page opts in to overlaying the keyboard. It gives the keyboard's height so CSS can pad or reposition content. Other browsers do not provide it, so always include a fallback.",[34,862,864],{"id":863},"related","Related",[55,866,867,872,878,885],{},[58,868,869,871],{},[23,870,26],{"href":25}," — the parent guide.",[58,873,874,877],{},[23,875,876],{"href":709},"dvh, svh and lvh: Mobile Viewport Units"," — the units used here.",[58,879,880,884],{},[23,881,883],{"href":882},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Foverscroll-behavior-for-nested-scrolling\u002F","Overscroll Behavior for Nested Scrolling"," — keeping the thread's scroll contained.",[58,886,887,891],{},[23,888,890],{"href":889},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fsticky-footer-with-flexbox-and-grid\u002F","Sticky Footer With Flexbox and Grid"," — the same row structure on desktop.",[893,894,895],"style",{},"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 .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .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}",{"title":194,"searchDepth":223,"depth":223,"links":897},[898,899,900,901,902,903,904,905,906,907],{"id":36,"depth":223,"text":37},{"id":179,"depth":223,"text":180},{"id":607,"depth":223,"text":608},{"id":678,"depth":223,"text":679},{"id":714,"depth":223,"text":715},{"id":734,"depth":223,"text":735},{"id":790,"depth":223,"text":791},{"id":797,"depth":223,"text":798},{"id":829,"depth":223,"text":830},{"id":863,"depth":223,"text":864},"Keep chat inputs, bottom bars and full-height layouts usable when the on-screen keyboard opens: interactive-widget, dvh, keyboard insets and fallbacks.","md",{"pageTitle":5,"datePublished":911,"dateModified":911,"faq":912},"2026-09-18",[913,915,917,919],{"q":835,"a":914},"By default most mobile browsers overlay the keyboard on the page or resize only the visual viewport, leaving the layout viewport, and anything fixed to its bottom, where it was. The bar stays positioned against a viewport that is now partly covered.",{"q":841,"a":916},"It is a value in the viewport meta tag that asks the browser to shrink the layout viewport when the on-screen keyboard opens. Fixed elements and dvh-based heights then adapt to the space above the keyboard. Browser support varies, so treat it as an enhancement.",{"q":850,"a":918},"Only when the browser resizes the layout viewport for the keyboard, for example with interactive-widget=resizes-content. By default, dynamic viewport units track browser toolbars appearing and disappearing, not the keyboard.",{"q":859,"a":920},"An environment variable exposed by the VirtualKeyboard API in Chromium-based browsers when a page opts in to overlaying the keyboard. It gives the keyboard's height so CSS can pad or reposition content. Other browsers do not provide it, so always include a fallback.","\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget",{"title":5,"description":908},"mastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout\u002Fvirtual-keyboard-and-interactive-widget\u002Findex","eCI2LCPMowH_uEVHvdo5pNkP0ClzxQJyeKClSpQzQIo",[926,929,932,935,938,941,944,947,950,953,956,959,962,965,968,971,974,977,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085,1088,1091,1094,1097,1100,1103,1106,1109,1112,1115,1118,1121,1124,1127,1130,1133,1136,1139,1142,1145,1148,1151,1154,1157,1160,1163,1166,1169,1172,1175,1178,1181,1184,1187,1190,1193,1196,1199,1202,1205,1208,1211,1214,1217,1220,1223,1226,1229,1232,1235,1238,1241,1244,1247,1250,1253,1256,1259,1262,1265,1268,1271,1274,1277,1280,1283,1286,1289,1292,1295,1298,1301,1304,1307,1310,1313,1316,1319,1322,1325,1328,1331,1334,1337,1340,1343,1346,1348,1351,1354,1357,1360,1363,1366,1369,1372,1375,1378,1381,1384,1387,1390,1393,1396,1399,1402,1405,1408,1411,1414,1417,1420,1423,1426,1429,1432,1435,1438,1441,1444,1447,1450,1453,1456,1459,1462,1465,1468,1471,1474,1477,1480,1483],{"path":927,"title":928},"\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":930,"title":931},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fflashing-content-and-seizure-thresholds","Flashing Content and Seizure Thresholds",{"path":933,"title":934},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fforced-colors-mode-and-animations","Forced Colors Mode and Animations",{"path":936,"title":937},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations","Accessibility in CSS Animations: Patterns, Specs & Best Practices",{"path":939,"title":940},"\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":942,"title":943},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes","prefers-reduced-motion Recipes: A Reduction for Every Common Effect",{"path":945,"title":946},"\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":948,"title":949},"\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":951,"title":952},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns","Vestibular-Safe Animation Patterns: Why Some Motion Makes People Sick",{"path":954,"title":955},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fwcag-motion-success-criteria","WCAG Motion Success Criteria: What Each One Actually Requires",{"path":957,"title":958},"\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":960,"title":961},"\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":963,"title":964},"\u002Fcss-only-micro-interactions-animations\u002Fclip-path-and-mask-animations","Clip-Path & Mask Animations: Animating Visibility Instead of Position",{"path":966,"title":967},"\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":969,"title":970},"\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":972,"title":973},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fanimating-gradients","Animating CSS Gradients: Smooth Colour Motion Without Jumps",{"path":975,"title":976},"\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":978,"title":979},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions","Color & Theme Transitions: Colour as Part of the Motion System",{"path":981,"title":982},"\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":984,"title":985},"\u002Fcss-only-micro-interactions-animations\u002Fcolor-and-theme-transitions\u002Fsmooth-theme-switching-transitions","Smooth Theme Switching: Crossfades, Circular Reveals and No Flash",{"path":987,"title":988},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fcontainer-query-hover-affordances","Container Query Hover Affordances: When Narrow Means Persistent, Not Hidden",{"path":990,"title":991},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion","Container-Aware Motion: Animation Sized by the Space a Component Actually Has",{"path":993,"title":994},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Flayout-change-animations-at-container-breakpoints","Animating Changes at Container Breakpoints",{"path":996,"title":997},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fmotion-driven-by-container-style-queries","Motion Driven by Container Style Queries",{"path":999,"title":1000},"\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":1002,"title":1003},"\u002Fcss-only-micro-interactions-animations\u002Fcontainer-aware-motion\u002Fsuppressing-motion-in-small-containers","Suppressing Motion in Small Containers: Deliberately Animating Nothing",{"path":1005,"title":1006},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fcontrolling-css-animations-from-javascript","Controlling CSS Animations From JavaScript",{"path":1008,"title":1009},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api","CSS Animation vs the Web Animations API: A Decision Guide",{"path":1011,"title":1012},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwaapi-vs-css-for-interruptible-ui","Interruptible UI: CSS Transitions vs WAAPI",{"path":1014,"title":1015},"\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":1017,"title":1018},"\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":1020,"title":1021},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fcustom-property-fallbacks-and-invalid-values","Custom Property Fallbacks and Invalid Values",{"path":1023,"title":1024},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations","Fluid Spacing Tokens That Drive Transition Durations",{"path":1026,"title":1027},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture","CSS Custom Properties Architecture: Scalable Design Systems & Dynamic UI",{"path":1029,"title":1030},"\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":1032,"title":1033},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fscoping-custom-properties-to-components","Scoping Custom Properties to Components",{"path":1035,"title":1036},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions","CSS Transition Timing Functions: ease, cubic-bezier(), steps() and linear()",{"path":1038,"title":1039},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals","CSS Transition Fundamentals: Architecture, Performance & Patterns",{"path":1041,"title":1042},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Finterrupted-and-reversing-transitions","Interrupted and Reversing Transitions: What Happens Mid-Flight",{"path":1044,"title":1045},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fstarting-style-entry-animations","@starting-style: Animating an Element's Very First Style Change",{"path":1047,"title":1048},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-delay-choreography","Choreographing Motion With transition-delay",{"path":1050,"title":1051},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransition-shorthand-and-multiple-properties","The transition Shorthand and Multiple Properties",{"path":1053,"title":1054},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete","Transitioning display with transition-behavior: allow-discrete and @starting-style",{"path":1056,"title":1057},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fanimating-dialog-open-and-close","Animating \u003Cdialog> Open and Close: Both Directions, No Library",{"path":1059,"title":1060},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fbackdrop-animations","Animating ::backdrop: Fades and Blurs Behind Dialogs and Popovers",{"path":1062,"title":1063},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations","Dialog & Popover Animations: Motion for the Top Layer",{"path":1065,"title":1066},"\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":1068,"title":1069},"\u002Fcss-only-micro-interactions-animations\u002Fdialog-and-popover-animations\u002Fpopover-entry-and-exit-transitions","Popover Menus That Animate From Their Anchor",{"path":1071,"title":1072},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fchoosing-animation-durations","Choosing Animation Durations: How Long Motion Should Last",{"path":1074,"title":1075},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fcubic-bezier-overshoot-and-anticipation","Overshoot and Anticipation With cubic-bezier(): Motion With Character",{"path":1077,"title":1078},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design\u002Fentry-vs-exit-easing","Entry vs Exit Easing: Different Curves for Arriving and Leaving",{"path":1080,"title":1081},"\u002Fcss-only-micro-interactions-animations\u002Feasing-and-motion-design","Easing & Motion Design: Making CSS Motion Feel Intentional",{"path":1083,"title":1084},"\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":1086,"title":1087},"\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":1089,"title":1090},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips","Accessible CSS-Only Tooltips and Hover Cards With  and ",{"path":1092,"title":1093},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fbutton-press-and-active-states","Button Press and  States",{"path":1095,"title":1096},"\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":1098,"title":1099},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-within-form-patterns"," Form Patterns: Styling a Group From the Field Inside It",{"path":1101,"title":1102},"\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":1104,"title":1105},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design","Hover & Focus State Design: Spec-Compliant Patterns for Modern UIs",{"path":1107,"title":1108},"\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":1110,"title":1111},"\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":1113,"title":1114},"\u002Fcss-only-micro-interactions-animations","CSS-Only Micro-Interactions & Animations: Architecture, Performance & Implementation",{"path":1116,"title":1117},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fanimation-composition-and-stacking","Stacking Animations With animation-composition",{"path":1119,"title":1120},"\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":1122,"title":1123},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations","Container-Query-Triggered Keyframe Animations: Animate Differently per Available Space",{"path":1125,"title":1126},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-accordions-and-disclosure","CSS-Only Accordions and Disclosure Widgets with details and summary",{"path":1128,"title":1129},"\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":1131,"title":1132},"\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":1134,"title":1135},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns","Keyframe Animation Patterns: Spec-Compliant Architectures for Modern UIs",{"path":1137,"title":1138},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fmulti-step-keyframes-and-per-keyframe-easing","Multi-Step Keyframes and Per-Keyframe Easing",{"path":1140,"title":1141},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties","Staggered List Animations Using --i Index Custom Properties",{"path":1143,"title":1144},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fanimating-box-shadow-and-filters-cheaply","Animating box-shadow and Filters Cheaply",{"path":1146,"title":1147},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcontain-property-for-animation-performance","The contain Property for Animation Performance",{"path":1149,"title":1150},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fcss-triggers-layout-paint-composite","Layout, Paint and Composite: What Triggers What",{"path":1152,"title":1153},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration","Performance & GPU Acceleration in CSS: A Developer’s Blueprint",{"path":1155,"title":1156},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps","Optimizing CSS Animations for 60fps: Budgeting the Frame",{"path":1158,"title":1159},"\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":1161,"title":1162},"\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":1164,"title":1165},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations","Scroll-Driven Animations: Binding Motion to Scroll Position in Pure CSS",{"path":1167,"title":1168},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-animation-fallbacks","Shipping Scroll-Driven Animations Without Breaking Anything",{"path":1170,"title":1171},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-driven-parallax-effects","Scroll-Driven Parallax Effects in CSS",{"path":1173,"title":1174},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-progress-bar-without-javascript","A Reading Progress Bar Driven by scroll(root)",{"path":1176,"title":1177},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fscroll-triggered-reveal-animations","Reveal-on-Scroll with view() and animation-range",{"path":1179,"title":1180},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Ftimeline-scope-for-cross-element-animations","timeline-scope for Cross-Element Animations",{"path":1182,"title":1183},"\u002Fcss-only-micro-interactions-animations\u002Fscroll-driven-animations\u002Fview-timeline-and-animation-range","View Timelines and animation-range",{"path":1185,"title":1186},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fcross-document-view-transitions","Cross-Document View Transitions",{"path":1188,"title":1189},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers","View Transitions for CSS Developers: the Pseudo-Element Tree, Snapshots, and Named Elements",{"path":1191,"title":1192},"\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":1194,"title":1195},"\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":1197,"title":1198},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transition-types-and-classes","View Transition Types and Classes",{"path":1200,"title":1201},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-for-list-reordering","View Transitions for List Reordering",{"path":1203,"title":1204},"\u002Fcss-only-micro-interactions-animations\u002Fview-transitions-for-css-developers\u002Fview-transitions-with-reduced-motion","Honouring prefers-reduced-motion in View Transitions",{"path":1206,"title":1207},"\u002F","Modern CSS Layouts & Micro-Interactions",{"path":1209,"title":1210},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioned-tooltips","Anchor-Positioned Tooltips: Tethering, Flipping, and Getting the Semantics Right",{"path":1212,"title":1213},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-positioning-fallbacks","Anchor Positioning Fallbacks: A Graceful Floor for Older Browser Versions",{"path":1215,"title":1216},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fanchor-size-for-matching-widths","anchor-size(): Overlays That Match Their Trigger's Size",{"path":1218,"title":1219},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays","CSS Anchor Positioning and Overlays: Tethering Elements Without JavaScript",{"path":1221,"title":1222},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-attribute-and-css-styling","The popover Attribute: Top-Layer Overlays With No JavaScript",{"path":1224,"title":1225},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fpopover-light-dismiss-and-stacking","Popover Light Dismiss, Nesting and Stacking",{"path":1227,"title":1228},"\u002Fmastering-container-queries-responsive-layouts\u002Fanchor-positioning-and-overlays\u002Fposition-area-grid-explained","position-area: The Anchor Placement Grid",{"path":1230,"title":1231},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fcontainer-query-polyfill-tradeoffs","Container Query Polyfills: What They Cost and When to Skip Them",{"path":1233,"title":1234},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports","Feature Detection with @supports: Syntax, Semantics, and the Traps",{"path":1236,"title":1237},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers","Handling Container Query Fallbacks for Older Browsers",{"path":1239,"title":1240},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks","Container Query Fallbacks: Spec-Compliant CSS Strategies for Legacy Browsers",{"path":1242,"title":1243},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fintrinsic-layout-fallbacks-without-queries","Intrinsic Layout Fallbacks: Baselines That Adapt Without Any Query",{"path":1245,"title":1246},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ftesting-fallbacks-without-old-browsers","Testing CSS Fallbacks Without an Old Browser",{"path":1248,"title":1249},"\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":1251,"title":1252},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-range-and-logic-operators","Container Query Range Syntax and Logic Operators",{"path":1254,"title":1255},"\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":1257,"title":1258},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-type-size-vs-inline-size","Choosing container-type: inline-size, size, or normal",{"path":1260,"title":1261},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison","Container vs Media Queries: Choosing the Right Reference Box",{"path":1263,"title":1264},"\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":1266,"title":1267},"\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":1269,"title":1270},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics","Container Query Syntax Basics",{"path":1272,"title":1273},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries","Nesting and Naming Container Queries: Targeting the Right Ancestor",{"path":1275,"title":1276},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fauto-fit-minmax-responsive-grids","Responsive Grids with repeat(auto-fit, minmax())",{"path":1278,"title":1279},"\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":1281,"title":1282},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fgrid-auto-flow-dense-packing","grid-auto-flow: dense for Packed Layouts",{"path":1284,"title":1285},"\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":1287,"title":1288},"\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":1290,"title":1291},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts","CSS Grid and Subgrid Layouts for Responsive Interfaces",{"path":1293,"title":1294},"\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":1296,"title":1297},"\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":1299,"title":1300},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fsubgrid-vs-nested-grid","Subgrid vs an Independently Nested Grid",{"path":1302,"title":1303},"\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":1305,"title":1306},"\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":1308,"title":1309},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-grow-and-flex-shrink-explained","flex-grow and flex-shrink Explained",{"path":1311,"title":1312},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflex-wrap-intrinsic-responsive-rows","Breakpoint-Free Responsive Rows With flex-wrap and flex-basis",{"path":1314,"title":1315},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns\u002Fflexbox-gap-and-spacing","Flexbox gap vs Margins: Spacing Items Without Edge Hacks",{"path":1317,"title":1318},"\u002Fmastering-container-queries-responsive-layouts\u002Fflexbox-layout-patterns","Flexbox Layout Patterns: One-Dimensional Layout for Real Components",{"path":1320,"title":1321},"\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":1323,"title":1324},"\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":1326,"title":1327},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fdebugging-clamp-values","Debugging clamp(): Values That Are Ignored, Stuck or Never Fluid",{"path":1329,"title":1330},"\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":1332,"title":1333},"\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":1335,"title":1336},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-type-accessibility-and-zoom","Fluid Type, Accessibility and Zoom: The vw-Only Trap",{"path":1338,"title":1339},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript","Fluid Typography Without JavaScript: A Precision CSS Reference",{"path":1341,"title":1342},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp","Fluid Typography with clamp(): A Practical Guide for Modern CSS",{"path":1344,"title":1345},"\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":1347,"title":31},"\u002Fmastering-container-queries-responsive-layouts",{"path":1349,"title":1350},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media","Using aspect-ratio for Responsive Media and Preventing Layout Shift",{"path":1352,"title":1353},"\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":1355,"title":1356},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques","Intrinsic Sizing Techniques: Modern CSS Layouts for Responsive UI",{"path":1358,"title":1359},"\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":1361,"title":1362},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained","min-content, max-content, and fit-content() Explained",{"path":1364,"title":1365},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fobject-fit-and-object-position","object-fit and object-position: Images That Fit Any Box",{"path":1367,"title":1368},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fpreventing-flex-and-grid-overflow","The min-width: auto Trap in Flex and Grid Layouts",{"path":1370,"title":1371},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens","Cascade Layers for Reset and Design Tokens",{"path":1373,"title":1374},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-vs-specificity-hacks","Cascade Layers vs Specificity Hacks: Ordering Instead of Escalating",{"path":1376,"title":1377},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcss-scope-for-component-styles","@scope for Component Styles: Short Selectors That Cannot Leak",{"path":1379,"title":1380},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies","Modern CSS Reset Strategies: A Spec-Compliant Foundation",{"path":1382,"title":1383},"\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":1385,"title":1386},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fwriting-a-minimal-modern-reset","Writing a Minimal Modern CSS Reset: Every Line Justified",{"path":1388,"title":1389},"\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":1391,"title":1392},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has\u002Fempty-and-loading-states-with-has","Empty and Loading States With ()",{"path":1394,"title":1395},"\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":1397,"title":1398},"\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":1400,"title":1401},"\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":1403,"title":1404},"\u002Fmastering-container-queries-responsive-layouts\u002Fparent-aware-layouts-with-has","Parent-Aware Layouts With :has(): Letting Containers React to Their Contents",{"path":1406,"title":1407},"\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":1409,"title":1410},"\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":1412,"title":1413},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables","Container Query Data Tables: Reflowing Tables to Cards",{"path":1415,"title":1416},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-sidebar-layouts","Container Query Sidebar Layouts That Travel Between Contexts",{"path":1418,"title":1419},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns","Responsive Component Patterns: Architecture & Implementation",{"path":1421,"title":1422},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-forms-with-container-queries","Responsive Forms with Container Queries: Reflow Without Losing the Wiring",{"path":1424,"title":1425},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-hero-sections-with-container-queries","Responsive Hero Sections With Container Queries",{"path":1427,"title":1428},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries","Responsive Navigation Without Media Queries Using Container Queries",{"path":1430,"title":1431},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-pricing-tables","Responsive Pricing Tables: Aligned Features, a Clear Recommendation, Any Width",{"path":1433,"title":1434},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Ftabs-that-become-accordions","Tabs That Become an Accordion: One Component, Two Layouts",{"path":1436,"title":1437},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts\u002Fcss-only-carousel-with-scroll-snap","A CSS-Only Carousel With Scroll Snap",{"path":1439,"title":1440},"\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":1442,"title":1443},"\u002Fmastering-container-queries-responsive-layouts\u002Fscroll-snap-and-overflow-layouts","Scroll Snap & Overflow Layouts: Designing Components That Scroll on Purpose",{"path":1445,"title":1446},"\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":1448,"title":1449},"\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":1451,"title":1452},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fcombining-size-and-style-queries","Combining Size and Style Container Queries",{"path":1454,"title":1455},"\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":1457,"title":1458},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state","Style Queries and Container State: Token-Driven Component Variants",{"path":1460,"title":1461},"\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":1463,"title":1464},"\u002Fmastering-container-queries-responsive-layouts\u002Fstyle-queries-and-container-state\u002Fstyle-query-component-variants","Component Variants With Style Queries: One Token, Many Looks",{"path":1466,"title":1467},"\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":1469,"title":1470},"\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":1472,"title":1473},"\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":1475,"title":1476},"\u002Fmastering-container-queries-responsive-layouts\u002Fviewport-units-and-mobile-layout","Viewport Units & Mobile Layout: Designing for the Screen You Actually Get",{"path":1478,"title":1479},"\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":1481,"title":1482},"\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":921,"title":5},{"title":1485,"caption":1486,"height":1487,"html":1488,"css":1489},"A chat layout sized to the dynamic viewport","Header, scrolling thread and composer as three grid rows filling 100dvh of the frame. The thread scrolls on its own; the composer stays in flow, so on a phone it is scrolled or resized into view above the keyboard.",340,"\n\u003Cdiv class=\"chat\">\n  \u003Cheader class=\"chat__header\">\u003Cstrong>Design review\u003C\u002Fstrong>\u003C\u002Fheader>\n  \u003Cdiv class=\"chat__messages\">\n    \u003Cp class=\"msg\">Can we check the layout with the keyboard open?\u003C\u002Fp>\n    \u003Cp class=\"msg msg--me\">Yes — the composer is a grid row, not fixed.\u003C\u002Fp>\n    \u003Cp class=\"msg\">And the thread scrolls inside its own row?\u003C\u002Fp>\n    \u003Cp class=\"msg msg--me\">Right, with overscroll-behavior: contain.\u003C\u002Fp>\n    \u003Cp class=\"msg\">Great. Scroll this thread to see the header stay put.\u003C\u002Fp>\n    \u003Cp class=\"msg msg--me\">The composer stays at the bottom of the row too.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"chat__composer\">\n    \u003Clabel class=\"sr\" for=\"m\">Message\u003C\u002Flabel>\n    \u003Cinput id=\"m\" type=\"text\" placeholder=\"Message\">\n    \u003Cbutton type=\"button\">Send\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>","\nbody { margin: 0; }\n.chat { display: grid; grid-template-rows: auto 1fr auto; block-size: 100vh; block-size: 100dvh; }\n.chat__header { padding: 0.75rem 1rem; border-block-end: 1px solid var(--demo-border); }\n.chat__messages { overflow-y: auto; overscroll-behavior: contain; padding: 1rem; display: grid; gap: 0.5rem; align-content: start; }\n.msg { margin: 0; padding: 0.5rem 0.75rem; border-radius: 12px; background: var(--demo-surface); border: 1px solid var(--demo-border); max-inline-size: 80%; justify-self: start; }\n.msg--me { justify-self: end; background: color-mix(in srgb, var(--demo-accent) 20%, var(--demo-surface)); }\n.chat__composer { display: flex; gap: 0.5rem; padding: 0.75rem 1rem; padding-block-end: max(0.75rem, env(safe-area-inset-bottom)); border-block-start: 1px solid var(--demo-border); }\n.chat__composer input { flex: 1; min-inline-size: 0; font: inherit; padding: 0.5rem 0.75rem; border-radius: 8px; border: 1px solid var(--demo-border); background: var(--demo-bg); color: var(--demo-fg); }\n.chat__composer button { font: inherit; padding: 0.5rem 1rem; border-radius: 8px; border: 0; background: var(--demo-accent); color: #fff; }\n.sr { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }",1789717744838]