Skip to content

My Chat Panel Got Clipped by the Fixed Navbar

Adityo Guni Waluyo

A fixed navbar takes no space in the document flow, so a 100dvh chat panel still ran short. The fix pairs xl:mt-14 with a 100dvh subtraction.

TL;DR

A fixed navbar floats above the document flow, so it occupies no space and the panel's top slid underneath it despite having the correct height. The fix pairs xl:mt-14 with xl:h-[calc(100dvh-3.5rem)], since h-dvh only answers how tall, not where content starts. Visually, padding and gaps were dropped for one border divider, creating an edge-to-edge three-column surface.

My three-column chat panel got clipped on wide screens. The right column holding source context stopped exactly under the navbar, and the top of the center column hid behind the navbar while it was still transparent, before any scrolling. In the inspector the panel height read full viewport, yet its position started at the very top of the screen. The root cause was not height but position: the navbar is fixed, so it takes no space in the document flow [1]. The fix ended up being just two Tailwind declarations at the xl breakpoint, and both were born from one understanding I had missed.

My Wrong Guess: h-dvh Should Be Enough

I started from a reasonable assumption. The dvh unit is the dynamic viewport: its height follows browser UI that appears and disappears, unlike vh which references the largest viewport [2]. Tailwind ships h-dvh which compiles to height: 100dvh [3]. For an app layout that must fill the screen, this sounded like the final answer.

In fact, h-dvh only answers "how tall", not "starting where". In an earlier commit I had already added the arbitrary class xl:h-[calc(100dvh-3.5rem)], expecting the panel to stop 3.5 rem above the viewport bottom, right beneath the navbar. What happened: the top of the panel still sat behind the navbar in the unscrolled state. I almost increased the subtraction, but the number was already correct. The assumption was what was wrong.

A Fixed Navbar Does Not Shift the Document Flow

Per the MDN documentation, an element with position fixed is taken out of the normal flow and positioned relative to the viewport, so it can float in place regardless of scrolling [1]. The consequence: a navbar with h-14 does not consume 3.5 rem of the page. Content below it still renders from the viewport's zero point. My panel, 100dvh minus 3.5 rem tall and placed from the top of the viewport, was guaranteed to run 3.5 rem short at the top. That is where xl:mt-14 comes in: the margin pushes the panel's starting point below the navbar edge, and the height subtraction stays at 3.5 rem. The two must travel as a pair.

// before: panel flush to the viewport, its top hidden under the fixed navbar
<div className="... xl:h-[calc(100dvh-3.5rem)] xl:overflow-hidden">

// after: shifted below the navbar, height still reduced by 3.5 rem
<div className="... xl:mt-14 xl:h-[calc(100dvh-3.5rem)] xl:overflow-hidden">

Why 3.5 rem? Because h-14 in Tailwind compiles from the spacing scale to 3.5 rem [3], and my navbar does use h-14. If the navbar height changes, both numbers above have to be recalculated together. The advantage of basing the height on dvh: when mobile browser UI changes, the panel adjusts without JavaScript [2].

Flush From Edge to Edge, One Border as the Only Divider

The second fix was about looks, not geometry. The old layout wrapped all three columns as separate cards with gaps and padding on every side, so a wide screen felt like a dashboard of floating windows. In this overhaul the container opens up: padding and gaps are removed at xl, the chat sidebar loses its rounded corners and left-right borders, and a single xl:border-r becomes the only divider. The context panel on the right actually gains breathing room with xl:my-4 xl:mr-4 so it keeps a margin from the edge. Below xl everything returns to the old card look, so small devices stay untouched.

The end result: one continuous working surface from the left edge to the right, split into three by two vertical lines, stopping exactly under the navbar. The pattern is reusable for any app panel that lives under a fixed header: measure the header height, subtract it from 100dvh, offset the content by the header height, and let a border do the dividing.

Sources

Related articles