Summit Themes
Blog

Mastering Tailwind CSS for perfect screen fit

Getting a layout to fill a screen sounds trivial until a phone browser's collapsible toolbar swallows your hero, or an image punches out of its container, or a full-page section scrolls an extra pixel nobody can explain. "Screen fit" is not one problem — it is a cluster of related decisions about height units, overflow, and aspect ratios. Tailwind CSS v4 gives you better tools for all of them, but the new APIs are different enough from v3 that it pays to know exactly what each class does before you reach for it.

This guide walks through the full toolkit: which height class to use and when, how to keep content from spilling, and how aspect ratios prevent the layout shifts that kill both perceived quality and Core Web Vitals scores.

The viewport height problem

For years, h-screen was the go-to for full-height sections. It maps to height: 100vh, and on desktop that is almost always correct. On mobile it is frequently wrong. The vh unit is calculated against the maximum viewport height — the measurement taken when the browser's address bar is fully hidden. When someone first loads your page, the address bar is visible and your "full-screen" section is taller than the actual visible area, causing a scroll. This is the bug that caused designers to add -mt-16 hacks for years.

Browsers now expose three new units that describe the viewport more precisely:

  • svh (small viewport height) — the viewport height when all browser UI is shown. Always fits the visible area, but leaves empty space on scroll when the toolbar retracts.
  • lvh (large viewport height) — the viewport height when all browser UI is hidden. Same behavior as the old vh, same clipping problem on first load.
  • dvh (dynamic viewport height) — updates continuously as the browser UI expands and contracts. The element truly fills the screen at every moment.

Tailwind v4 ships all three as utility classes for height, min-height, max-height, and even size:

<!-- Height -->
h-dvh   → height: 100dvh;
h-svh   → height: 100svh;
h-lvh   → height: 100lvh;

<!-- Min-height -->
min-h-dvh   → min-height: 100dvh;
min-h-svh   → min-height: 100svh;
min-h-lvh   → min-height: 100lvh;

Choosing the right class for your section

Hero sections almost always want min-h-dvh, not h-dvh. A fixed height cuts off content if the user bumps up the font size or if you add a few more words to the headline. A minimum height lets the section grow while still filling the screen when content is short.

<section class="min-h-dvh flex flex-col items-center justify-center">
  <h1 class="text-5xl font-bold">Ready to launch</h1>
  <p class="mt-4 text-lg">Your site, live today.</p>
</section>

Full-page app shells that should never scroll the outer wrapper can use h-dvh overflow-hidden and let the inner content area scroll independently with overflow-y-auto:

<div class="h-dvh flex flex-col overflow-hidden">
  <header class="shrink-0 h-16">...</header>
  <main class="flex-1 overflow-y-auto">...</main>
</div>

The shrink-0 on the header prevents flex from squashing it when content is tall. flex-1 on main means it takes all remaining height.

When to keep h-screen: desktop-only dashboards and splash pages where mobile layout is either suppressed or handled separately. h-screen still works fine in those cases and has 100% browser support. Use the newer units when you need them, not by default.

Full-width without horizontal scroll

Tailwind has a w-screen class (width: 100vw), and it causes horizontal scroll more often than not. The browser calculates 100vw as the full viewport width including the scrollbar. If a vertical scrollbar is present, w-screen overflows by roughly 15–17px.

For most layouts, use w-full (width: 100%) and let the parent define the boundary. Reserve w-screen for positioned elements that need to break out of a padded container — and pair it with a negative margin to cancel the offset:

<div class="w-screen -mx-4 sm:-mx-8 lg:-mx-16">
  <!-- Full-bleed image or banner -->
</div>

A cleaner modern approach for full-bleed sections is the CSS logical property trick, but the class above works in any Tailwind v4 project without extra configuration.

Aspect ratios: the layout-shift killer

When a browser renders an image before it has fetched the file, it renders it with zero height. The page jumps when the image loads. Cumulative Layout Shift (CLS) is one of Google's Core Web Vitals — a high score hurts ranking. The fix is reserving space in advance with a declared aspect ratio.

Tailwind v4 provides three built-in aspect ratio classes and a clean path to custom ones:

aspect-square   → aspect-ratio: 1 / 1;
aspect-video    → aspect-ratio: 16 / 9;
aspect-auto     → aspect-ratio: auto;

For non-standard ratios, use the bracket syntax for arbitrary values:

<!-- 4:3 photo ratio -->
<div class="aspect-[4/3] overflow-hidden">
  <img src="photo.jpg" class="h-full w-full object-cover" alt="..." />
</div>

For a custom ratio used throughout your project, define it once with the v4 CSS-first @theme directive so you get a real utility class:

/* In your main CSS file, inside @layer or at root level */
@theme {
  --aspect-retro: 4 / 3;
  --aspect-portrait: 3 / 4;
  --aspect-cinema: 21 / 9;
}

Those tokens generate aspect-retro, aspect-portrait, and aspect-cinema as usable classes immediately — no plugin, no rebuild configuration, no JavaScript config file needed. That is the CSS-first workflow Tailwind v4 was designed around.

Object-fit: filling versus fitting

Declaring an aspect ratio on a container does not control how media inside it behaves. You need object-fit for that. The two classes you will reach for constantly:

  • object-cover — the image fills the container completely, cropping the edges if the aspect ratios differ. Use this for hero backgrounds, team photos, and card thumbnails.
  • object-contain — the entire image is visible, with letterboxing on the sides or top/bottom. Use this for logos, product shots with transparent backgrounds, and icons.
<!-- Card thumbnail: crop to fill, no distortion -->
<div class="aspect-video overflow-hidden rounded-xl">
  <img
    src="theme-preview.jpg"
    alt="Theme screenshot"
    class="h-full w-full object-cover"
  />
</div>

<!-- Logo: show all of it, no crop -->
<div class="aspect-[3/1] flex items-center">
  <img
    src="logo.svg"
    alt="Company logo"
    class="h-full w-auto object-contain"
  />
</div>

Always add overflow-hidden to the container when using object-cover. Without it, the image can overflow its box on some browsers, especially when combined with rounded-* classes.

A complete hero pattern

Putting it all together: a sticky-header layout with a proper full-screen hero that works on desktop and mobile alike.

<!-- Outer shell: exactly the dynamic viewport, no outer scroll -->
<div class="flex flex-col h-dvh">

  <!-- Sticky header -->
  <header class="shrink-0 sticky top-0 z-50 h-16 bg-white border-b">
    <nav class="h-full flex items-center justify-between px-6">
      <span class="font-bold">Brand</span>
      <a href="/contact" class="text-sm">Contact</a>
    </nav>
  </header>

  <!-- Scrollable content area -->
  <main class="flex-1 overflow-y-auto">

    <!-- Hero fills the screen below the header -->
    <section class="min-h-[calc(100dvh-4rem)] flex flex-col items-center justify-center px-6 text-center">
      <h1 class="text-4xl font-bold tracking-tight">Local business sites, ready to launch</h1>
      <p class="mt-4 max-w-prose text-base text-gray-600">
        Production-ready Astro themes for service businesses.
      </p>
      <a href="/templates" class="mt-8 px-6 py-3 bg-black text-white rounded-full text-sm">
        Browse themes
      </a>
    </section>

    <!-- Rest of page content... -->

  </main>
</div>

The hero uses calc(100dvh - 4rem) as an arbitrary min-height to subtract the 4rem header. Tailwind v4 accepts arbitrary values in bracket syntax everywhere, so this requires no custom configuration.

Responsive overrides

All of these utilities accept Tailwind's breakpoint prefixes. A common pattern: use a natural content height on mobile where fixed-height sections can feel claustrophobic, then enforce the full-screen behavior on larger screens:

<section class="py-20 md:min-h-dvh md:py-0 flex flex-col items-center justify-center">
  ...
</section>

On small screens the section breathes with py-20 padding. On medium and up it pins to the full dynamic viewport height and centers its content.

A note on browser support

The dvh, svh, and lvh units are supported in all modern browsers as of 2023 — Chrome 108, Firefox 101, Safari 15.4. If your analytics show meaningful traffic from older browsers (pre-2022 mobile), pair the new unit with a vh fallback using Tailwind's arbitrary value syntax or a one-line custom utility in your CSS. For most service business sites launched today, the new units are safe without a fallback.

The aspect-ratio property has similar support — it is available in all evergreen browsers and has been for several years. Do not hold back on using it.

Summary

The pattern to internalize: use min-h-dvh for sections that should fill the screen (not h-screen unless you have a specific reason), use aspect-* plus object-cover on every media container to prevent layout shift and overflow, keep w-full instead of w-screen for full-width sections, and define recurring custom tokens once in @theme so they generate real utility classes. These four habits remove an entire category of layout bugs and make layouts measurably more stable across devices.