Tailwind Height: 139 Uses, Zero of Them Plain h-screen
This codebase's h-* utilities are mostly icon sizing. The one true viewport lock is h-[100dvh], not h-screen — because a static 100vh lies on mobile.
Tailwind's height utilities set an element's height property from a fixed scale (h-4, h-12, h-full) or an arbitrary value (h-[100dvh]), the same way w-* sets width. This codebase uses 139 h-* instances across 22 distinct values — mostly square icon sizing — and exactly one true viewport-height lock, written as h-[100dvh] rather than the more commonly taught h-screen.
The short answer
h-<value> maps to CSS height. The fixed scale (h-0, h-1, …, h-96) multiplies Tailwind's spacing unit; keyword values cover common cases (h-full → 100%, h-screen → 100vh, h-dvh → 100dvh, h-fit → fit-content); and square brackets take any CSS value verbatim (h-[520px], h-[calc(100vh-7rem)]). min-h-* and max-h-* follow the identical naming for min-height/max-height.
What this codebase's height utilities actually look like
139 total h-* instances, 22 distinct values
19 h-9 18 h-7 18 h-4 15 h-full
13 h-5 13 h-12 8 h-10 5 h-0.5
4 h-8 4 h-6 4 h-5.5 4 h-4.5
3 h-6.5 2 h-px 2 h-13 1 h-60
1 h-44 1 h-[calc(100vh-7rem)] 1 h-[100dvh]
1 h-[520px] 1 h-[180px] 1 h-[140px]
The top of that list isn't page layout at all — h-9, h-7, h-4, h-5, h-12 are square icon dimensions, always paired with an identical w-* (h-12 w-12 on every FeatureCard icon, h-7 w-7 md:h-9 md:w-9 lg:h-7 lg:w-7 xl:h-9 xl:w-9 on FeaturePill's responsive icon sizing). Icon sizing, not section height, is where most of this project's h-* usage actually goes.
h-full — the second-most-common value, and why
h-full (15 uses) sets height: 100%, which only resolves to something visible if every ancestor up to a defined height also has one — Tailwind's own docs flag this as the single most common h-full mistake. FeatureCard's outer wrapper uses it for exactly the case where that requirement is already met:
// src/components/molecules/FeatureCard.tsx
<div className="rounded-3xl border border-stroke-secondary bg-gray-50 p-1 ...">
<div className="h-full rounded-2xl border border-[#F2F4F7] bg-white p-4 md:p-6">
...
</div>
</div>
The inner h-full div fills its direct parent, which itself is one cell of a CSS grid — the grid track gives every card in a row the same height regardless of how much text it holds, and h-full just tells the card's inner border to match it. No JavaScript measures anything; the grid computes the row height and h-full inherits it in one property.
No h-screen anywhere — min-h-screen and one real h-[100dvh] instead
Zero occurrences of bare h-screen in this codebase, despite it being the utility every "full-height hero" tutorial reaches for first. What actually appears:
// src/app/login/page.tsx, signup, forgot-password, reset-password, error.tsx — 5 sites
<section className="flex min-h-screen items-center justify-center pt-28 pb-16">
min-h-screen (5 sites, all auth pages plus the error page) says "at least one viewport tall, grow if the content needs more" — the right choice for a centered form that must never clip, since h-screen would cap the section at exactly 100vh and clip a tall validation-error state.
The one place this repo does lock an element to the exact device height is the live-preview modal, and it doesn't use h-screen either:
// src/components/molecules/PreviewModal.tsx
className="fixed inset-0 z-[10000] flex h-[100dvh] flex-col overscroll-none bg-gray-900/80 backdrop-blur-sm"
100dvh — the dynamic viewport height unit — recalculates as a mobile browser's chrome (address bar, bottom toolbar) shows or hides, where the older 100vh freezes at the tallest possible viewport and leaves a gap once the browser chrome reappears. h-screen compiles to 100vh; h-dvh is Tailwind's core utility for the same idea using 100dvh. This modal reaches for the arbitrary-value spelling, h-[100dvh], which resolves identically — the fixed, full-bleed overlay that needs to track the real visible viewport on a phone is exactly the one element in this codebase where the vh/dvh distinction is load-bearing rather than academic.
One calc() and three decorative pixel heights
DocsSidebar locks its sticky nav to the remaining viewport below the header, computed rather than guessed:
// src/components/organisms/DocsSidebar.tsx
<aside className="shrink-0 lg:sticky lg:top-24 lg:h-[calc(100vh-7rem)] lg:w-64 lg:overflow-y-auto lg:pr-2">
top-24 (6rem) plus a further 1rem of clearance is subtracted from 100vh, so the sidebar's own scroll region never runs taller than the space actually left below the sticky header — without that subtraction, h-screen alone would let the sidebar's content push past the viewport bottom before its own overflow-y-auto ever kicks in.
The remaining three arbitrary values are unapologetically decorative: HeroBackground.tsx sizes three stacked gradient shapes at h-[520px], h-[180px], and h-[140px] — pixel values chosen to match a specific visual composition, not a layout constraint, which is exactly the case arbitrary values exist for for.
Comparison: the height keywords this codebase actually needed
| Utility | Resolves to | Used here | Where |
|---|---|---|---|
h-full | 100% of parent | 15× | Grid-cell-filling cards, icon wrappers |
min-h-screen | min-height: 100vh | 5× | Auth pages, error page (centers content, never clips) |
h-[100dvh] | height: 100dvh | 1× | PreviewModal — locks to the real mobile viewport |
h-[calc(100vh-7rem)] | Computed | 1× | DocsSidebar — viewport minus header clearance |
h-screen (100vh, static) | — | 0× | Not used — every viewport-height need here needed either "at least" (min-h-screen) or "the dynamic real one" (h-dvh) |
The absence of plain h-screen is the interesting row: every place this codebase reached for a viewport-relative height, the static 100vh was the wrong tool — either because the content could overflow it (min-h-screen) or because a mobile browser's chrome makes 100vh lie about what's actually visible (h-[100dvh]).
The percentage-height trap this codebase avoids
Tailwind's own docs call out h-full (height: 100%) as commonly misunderstood, and it's worth spelling out why in concrete terms rather than the abstract. A percentage height only resolves against a parent that itself has a defined height — not "however tall its content happens to make it," but an explicit value, 100% of something further up the chain, or a height derived from a layout mode like grid or flex that hands each item a track size. FeatureCard's h-full works precisely because its immediate parent is a CSS grid cell, which always has a defined size the moment the grid itself lays out — there's no ambiguity to inherit. Drop that same h-full div into ordinary block-flow markup with no explicit height anywhere above it, and the height collapses to whatever the content needs, silently ignoring the class. Nothing in Tailwind warns about this at build time, because from the compiler's point of view h-full compiled correctly — height: 100% is valid CSS whether or not an ancestor happens to satisfy it.
That's also why this repo has no bare h-<number> giants pretending to be full-height sections. A hard-coded h-[900px] "full height" hero would break the instant copy length changed on a different screen size; every place this codebase needs "fill the available space," it either sits inside a grid/flex parent that defines that space (h-full) or names the viewport explicitly (min-h-screen, h-[100dvh]) rather than guessing a pixel number that would inevitably drift.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
h-full has no visible effect | An ancestor in the chain has no defined height, so 100% resolves against nothing | Give the parent an explicit height, or switch to h-screen/h-dvh/min-h-screen if the intent was viewport-relative |
| A full-height mobile overlay leaves a gap when the address bar shows or hides | h-screen (100vh) was used for a fixed, full-bleed element | Use h-dvh (or h-[100dvh]) instead — this is exactly why PreviewModal does |
| A centered form clips its own error state on a short viewport | h-screen was used instead of min-h-screen | Swap to min-h-screen so the section grows past one viewport when content needs it, as every auth page here does |
h-screen renders shorter than expected on iOS Safari | Classic 100vh bug — it's measured against the largest possible viewport, ignoring collapsed browser chrome | h-dvh (100dvh) tracks the actual visible viewport and is the fix, not a JS window.innerHeight polyfill |
An arbitrary h-[calc(...)] value silently does nothing | A typo or unsupported expression inside the brackets, or a missing unit on one operand | Confirm the expression is valid CSS calc() syntax exactly as written — Tailwind passes arbitrary values through unmodified, so a broken calc() fails the same way it would in plain CSS |
Prettier reorders classes around an h-* utility unexpectedly | prettier-plugin-tailwindcss sorts by Tailwind's own internal class order, not alphabetically | Expected — per this repo's convention, class order is cosmetic as long as every class survives |
Frequently asked questions
What's the difference between h-screen and h-dvh in Tailwind?
h-screen compiles to height: 100vh, measured once against the largest possible viewport. h-dvh compiles to height: 100dvh, which recalculates as a mobile browser's UI chrome shows or hides. This codebase's one full-viewport-lock element (PreviewModal) uses the dvh behavior specifically because it targets phones.
When should I use h-full instead of h-screen?
h-full fills whatever height the parent has already established (a grid row, a flex sibling); h-screen/h-dvh fill the actual browser viewport regardless of parent. This repo's card grids use h-full because the grid track — not the viewport — defines the height that matters.
Why does min-h-screen appear instead of h-screen on every auth page here?
Because the content (a form plus a validation error) can be taller than one viewport, and h-screen would clip it at exactly 100vh. min-h-screen guarantees at least a full viewport while still growing for taller content.
Is h-[calc(100vh-7rem)] better than just estimating a fixed pixel height?
Yes when the subtracted amount is a real, known layout value — here, the sticky header's height plus clearance. A guessed fixed height would drift out of sync the moment the header's own height changed; the calc() stays correct because it's expressed in terms of the actual constraint.
Templates in this post
ASoc Beacon, ASoc Beaker and ASoc Blueprint are Next.js + Tailwind landing page templates using the same height conventions audited above — icon sizing on h-*/w-* pairs, sections on min-h-screen, never a bare h-screen clipping content short.
Browse the full sets: Next.js landing page templates, Tailwind landing page templates.
