Responsive Webflow Navbar: dvh Units & Scroll Lock

Intermediate 46:35 webflowresponsivenavbardvhscroll-lock

Turn a desktop Webflow navbar into a fully responsive mobile menu — fix the mobile height bug with dvh units, master flex wrapping, and lock page scroll when the menu is open.

Key takeaways

  • Use 100dvh, not 100vh, for a full-height mobile menu: vh ignores the phone's address/nav bars, so part of the menu hides behind them until you scroll.
  • Webflow forces the open nav menu to display: block, so margin-top: auto won't push a group to the bottom — wrap the content in an inner div and move the flex styles there.
  • Structural changes (adding a wrapper) affect every breakpoint, so re-home the layout styles onto the new element and check desktop didn't break.
  • flex-wrap only wraps items that are allowed to shrink: switch 'grow if possible' to 'shrink if needed' (or set explicit grow values) so buttons can drop to a new row.
  • Locking page scroll behind an open menu is a few lines of CSS that set the body to overflow: hidden while the menu is open — no JavaScript required.

Video chapters

  1. 00:00 Intro
  2. 01:20 Menu button, Flexbox, link underline
  3. 08:39 Open state and nav menu height
  4. 14:34 Vertical layout, spacing, alignment
  5. 21:34 Flex wrap + Webflow hidden property values
  6. 30:51 Landscape and Portrait breakpoints
  7. 34:08 vh and dvh units: nav menu height
  8. 39:09 Removing hover interactions
  9. 41:19 Blocking page scroll when the menu is open
  10. 45:17 Outro

By the end of this lesson the static desktop navbar from Part 1 becomes a fully responsive navigation system that works across tablet, landscape, and portrait — mobile menu included. We debug the layout as we go and dig into the units and quirks that make mobile menus genuinely hard: vh vs dvh, Webflow’s open-state behavior, and flex wrapping.

Two problems are worth the whole lesson on their own. The first is the mobile height bug: a menu sized with 100vh gets clipped by the phone’s browser bars. The second is the “push a group to the bottom” trick that mysteriously stops working when the menu opens. Both have clean fixes once you understand what’s actually happening.

We finish with a professional touch: a few lines of CSS that lock page scroll while the menu is open, so nothing drifts in the background.

How it works

The mobile height bug is the headline. We want the open nav menu to fill the screen below the navbar, so the instinct is calc(100vh - navbarHeight). But 100vh measures the viewport ignoring the mobile browser’s address bar and bottom navigation bar, so before you scroll, the bottom of the menu hides behind them. The fix is dvh (dynamic viewport height): it tracks the actually-visible area and updates live as those bars appear and disappear. Switch the height to calc(100dvh - navbarHeight) and the menu is never clipped. (Watch for inherited values — if you duplicated the nav-menu class onto an inner wrapper, reset the height in the right place.)

The bottom-group trick needs a workaround. To pin the secondary button group to the bottom of the menu, you’d normally give it margin-top: auto in a flex column. But when the menu is open, Webflow forces the nav menu to display: block, so it isn’t a real flex container and the auto-margin has nothing to push against. The fix: wrap the menu’s contents in an inner div, move the flex layout (display: flex, direction, gap, sizing) onto that wrapper, and leave the outer nav menu just holding its size. Because adding a wrapper is a structural change, it hits every breakpoint — so you re-home the styles and double-check desktop still looks right.

Flex wrapping has a catch. Enabling flex-wrap seems to do nothing when the children are set to grow if possible, because they’d rather shrink than drop to a new line. Switch them to shrink if needed (or set explicit flex-grow values) and they wrap as expected — e.g. two buttons on one row, the CTA on the next.

Scroll lock is tiny. A few lines of CSS set the body to overflow: hidden while the menu is open, freezing the page behind it. No JavaScript. Put it in the site’s head custom code (so it applies everywhere) or, if the navbar is a component, in an embed inside the component.

How to use it

  1. Reposition the menu button. Give it a class and push it right with margin-left: auto. Duplicate the “contact sales” link outside the menu, hide it on desktop with a cc-mobile combo class, and show it from tablet down.
  2. Fix the open state. The menu button changes color when open; select its open state and set the background transparent and the icon color to your dark variable so it matches the closed state.
  3. Size the menu correctly. Set the nav menu height to calc(100dvh - <navbarHeight>rem) (compute the navbar height from the link’s padding + line-height). Give it the right background.
  4. Go vertical. Set the primary link group to a vertical flex column, align: stretch so items fill the width, switch dropdowns to display: block, and left-align the link text. Move the dropdown arrow right with justify-content: space-between; add horizontal padding and item borders (transparent on the last via a cc-last combo).
  5. Bottom group + the wrapper fix. Add an inner navbar_nav-menu-inner-wrapper around the menu content, move the flex styles onto it (column direction), and give the secondary group margin-top: auto to sit at the bottom.
  6. Wrap on small screens. In landscape/portrait, enable flex-wrap on the button group and set the buttons to shrink if needed / explicit flex-grow: 1 so they lay out cleanly across rows.
  7. Strip hover. Remove the hover transitions, underline box-shadows, and color changes you added for desktop — they don’t belong on touch.
  8. Lock the scroll. Add the CSS snippet that sets body { overflow: hidden } while the menu is open (head custom code or a navbar embed), publish, and test on a real device.

Next episode: building the desktop mega menu itself.

Resources

Frequently asked questions

Why is my full-height mobile menu cut off by the browser bar?
You're likely using 100vh, which measures the viewport without accounting for the mobile browser's address and navigation bars. Switch to 100dvh (dynamic viewport height) — it tracks the actually-visible area in real time, so the menu resizes as those bars appear and disappear.
Why doesn't margin-top: auto push my group to the bottom of the Webflow nav menu?
When the menu is open, Webflow forces the nav menu to display: block, so it isn't a real flex container and the auto-margin trick has nothing to work against. Wrap the menu's content in an inner div, move the flex layout onto that wrapper, and the trick works again.
How do I make navbar buttons wrap to a new row in Webflow?
flex-wrap alone won't wrap items set to 'grow if possible', because they keep shrinking to stay on one line. Change their flex-child sizing to 'shrink if needed', or set an explicit flex-grow, so there's a point where they drop to the next row.
How do I stop the page scrolling behind an open mobile menu?
Add a small CSS rule that sets the body to overflow: hidden while the nav menu is open, either in the site's head custom code or an embed inside the navbar. It freezes the page behind the menu with no JavaScript.

← Back to the course