024: Sizing, Units, Constraints, and Overflow
Learning outcomes
By the end, you can choose absolute, font-relative, viewport, and percentage lengths; prefer constraints over rigid dimensions; create fluid media; explain intrinsic sizing; diagnose overflow; and preserve content at zoom and narrow widths.
Prerequisites and retrieval
Use yesterday's border-box setup. Calculate a card's occupied inline size from content, padding, border, and margin. Retrieve why fixed heights around text are risky. Today the card becomes fluid without Flexbox, Grid, or media queries.
Terminology
- Absolute length: A unit with fixed physical-ish definition, such as px (a CSS reference pixel). — Source: MDN: Values and units
- Relative length: Units depending on another measure: %, em, rem, ch, vw. — Source: MDN: Values and units
- Intrinsic size: Size determined by the element’s own content (min/max-content). — Source: CSS Sizing 3: Intrinsic sizes
- Extrinsic size: Size imposed by outside constraints rather than content. — Source: CSS Sizing Level 3
- Min/max constraint: Lower and upper bounds clamping a computed size. — Source: CSS Sizing Level 3
- Overflow: Content extending past its box’s bounds; governable via overflow properties. — Source: MDN: Overflowing content
- Replaced element: Element whose content is replaced by an external object (img, video). — Source: MDN: Sizing items in CSS
- Containing block: The rectangle against which an element’s offsets and percentage sizes resolve. — Source: MDN: Layout and the containing block
- Width (
width): "The width property specifies the width of the content area (or border area under border-box)." — Source: MDN: width - Fluid value (
clamp()): "clamp(MIN, VAL, MAX) clamps a value between an upper and lower bound." — Source: CSS Values and Units Level 4: clamp() - Viewport unit (
vw/dvh): "1vw = 1% of viewport width; dvh = dynamic viewport height." — Source: MDN: CSS values and units — Viewport units
Mental model: available space negotiating with content
Responsive sizing is a negotiation among available space, content's minimum needs, preferred size, and explicit constraints. width: 40rem demands one size. max-inline-size: 40rem; inline-size: 100% says “fill available inline space, but never exceed 40rem.” Usually the second relationship is more resilient.
Useful unit roles:
rem: typography, spacing, target dimensions tied to user font settings.em: local sizing tied to a component's text.%: relative to a containing block for many size properties.ch: readable text measure.px: thin borders and exact small details; still a CSS reference unit.- viewport units: useful when a design genuinely relates to viewport dimensions, but avoid forcing text containers to viewport heights.
Use min-*, preferred size, and max-* as guardrails. Content should usually determine block height.
Beginner example: fluid portfolio shell and media
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
margin: 0;
color: rgb(30 41 59);
background: rgb(248 250 252);
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.site-shell {
inline-size: 100%;
max-inline-size: 70rem;
margin-inline: auto;
padding-inline: 1rem;
}
.prose {
max-inline-size: 65ch;
}
img,
svg,
video {
display: block;
max-inline-size: 100%;
block-size: auto;
}
.project-card {
inline-size: 100%;
max-inline-size: 36rem;
min-block-size: 12rem;
padding: 1rem;
border: 1px solid rgb(203 213 225);
background: white;
}
<div class="site-shell">
<main>
<section class="prose">
<h1>Selected work</h1>
<p>Projects built with semantic HTML and resilient CSS.</p>
</section>
<article class="project-card">
<img src="images/library.jpg" width="960" height="540" alt="Library finder search results">
<h2>Library finder</h2>
<p>A long description may wrap to several lines without being clipped.</p>
</article>
</main>
</div>
The shell fills available width until 70rem and then auto margins share remaining space. Inline padding ensures content does not touch viewport edges. Media shrink to their container and retain aspect ratio. HTML image dimensions reserve the correct aspect-ratio space before download, reducing layout shift. min-block-size sets a floor but permits content to make the card taller; block-size: 12rem would risk clipping.
Resize the browser continuously. No breakpoint is needed for this behavior.
Intermediate example: intentional overflow
Overflow is evidence, not automatically a bug. Long unbreakable data can exceed a card:
<article class="project-card">
<h2>API explorer</h2>
<p class="resource-url">https://example.com/a-very-long-unbroken-resource-identifier-that-continues</p>
<pre><code>const result = await fetch("/projects?sort=recent");</code></pre>
</article>
.resource-url {
overflow-wrap: anywhere;
}
pre {
max-inline-size: 100%;
overflow-x: auto;
padding: 1rem;
background: rgb(15 23 42);
color: rgb(226 232 240);
}
Prose URLs may safely break anywhere. Code's whitespace can be meaningful, so a local horizontal scrollbar is preferable to wrapping or forcing the entire page wider. Do not put overflow-x: hidden on body to conceal the symptom; that can make off-screen content unreachable.
For a thumbnail crop:
.project-thumbnail {
inline-size: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 0.5rem;
}
The aspect ratio reserves proportional geometry; object-fit: cover crops the replaced image to fill it. Ensure the crop does not remove meaningful information. Use ordinary fluid images when the full screenshot matters.
Optional advanced example: bounded fluid values
clamp(minimum, preferred, maximum) is broadly supported:
.hero {
padding-block: clamp(2rem, 8vw, 6rem);
}
.hero h1 {
font-size: clamp(2rem, 1.5rem + 3vw, 4.5rem);
}
The preferred term grows with viewport width but never crosses the bounds. Keep a rem contribution in fluid text so zoom and user font settings remain influential. clamp() is a compact constraint, not a replacement for testing real content.
Viewport heights can be unstable on mobile as browser controls appear. If a full-height panel is truly required, modern dynamic viewport units such as dvh better follow visible viewport changes, but most content pages should use min-block-size or natural content height rather than a forced screen height.
Mistakes, debugging, and DevTools
- Fixed width wider than the viewport: replace it with a fluid preferred size plus
max-inline-size. width: 100vwon page content: viewport width can include scrollbar space and cause horizontal overflow;100%usually fits the containing block.- Fixed card height: text clips or overlaps under zoom. Prefer
min-block-sizeor no block size. - Unconstrained images: intrinsic pixel width can exceed the container.
- Global
overflow: hidden: hides evidence, clips focus, and can make content inaccessible. - Percentage height with no definite containing-block height: it may resolve unexpectedly.
- Using viewport-only font size: text can become too small or ignore zoom expectations.
In DevTools, enable responsive/device mode and drag width rather than checking only presets. Inspect computed used width, max width, and box sizing. Look for the element extending past the viewport; temporarily apply * { outline: 1px solid red; } only during debugging. The Layout/Rendering tools may expose scrollable overflow. Test long words, 200% and 400% zoom, larger default fonts, and translated text.
Accessibility and performance
WCAG Reflow expects content to work without two-dimensional scrolling at 320 CSS pixels wide (or 256 CSS pixels high for vertical writing), except content that inherently requires two-dimensional layout. A code sample can have a local scrollbar; the whole article should not.
Never clip essential text. Make focus indicators visible even near constrained edges. Allow controls and labels to wrap. rem dimensions adapt better to text preferences, but do not assume relative units alone guarantee reflow.
Provide image width and height attributes to reserve space and reduce cumulative layout shift, then constrain images with CSS. Serve appropriately sized compressed images rather than downloading a massive image and shrinking it visually. CSS sizing does not reduce file bytes.
Deep dive: choose units by what the value should depend on
Use the dependency as the decision:
px: device-independent CSS pixel; useful for thin borders and exact small details.rem: root font size; useful for consistent type/spacing scales.em: current element's font size; useful when a component should scale with its own text.%: relative to a property-specific reference, often a containing block.vw/vh: viewport width/height.svh/lvh/dvh: small, large, and dynamic viewport height variants for mobile browser UI behavior.ch: approximate width of the0glyph; useful for text measure.fr: Grid-only share of leftover track space.cqw/cqh: container query units once a query container exists.
Avoid choosing units by fashion. Ask what should happen when the user changes text size, the container shrinks, or browser chrome changes.
Deep dive: intrinsic sizing keywords
CSS can size from content:
.badge {
inline-size: max-content;
}
.search {
inline-size: min(100%, 32rem);
}
.panel {
inline-size: fit-content(40rem);
}
Grid and Flexbox also use intrinsic concepts such as min-content and max-content when deciding minimums and track sizes.
A famous Grid overflow fix:
.layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 18rem;
}
minmax(0, 1fr) explicitly permits the flexible track to shrink below its content-based automatic minimum.
Deep dive: math functions
calc()
.sidebar {
inline-size: calc(100% - 2rem);
}
calc() is valuable when different unit types must participate in one expression.
min() and max()
.shell {
inline-size: min(100% - 2rem, 72rem);
margin-inline: auto;
}
Read it as: choose the smaller of “viewport/container minus gutters” and “72rem”.
clamp()
.section {
padding-block: clamp(2rem, 5vw, 6rem);
}
clamp(min, preferred, max) is excellent for bounded fluid values.
Worked example: responsive shell with no media query
.shell {
width: min(100% - 2rem, 75rem);
margin-inline: auto;
}
.hero {
padding-block: clamp(3rem, 10vw, 8rem);
}
.hero h1 {
font-size: clamp(2.25rem, 1.5rem + 4vw, 5rem);
}
The page grows continuously instead of jumping between arbitrary device sizes.
Deep dive: aspect ratio and replaced elements
.thumbnail {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
}
aspect-ratio contributes a preferred width/height relationship. object-fit controls how replaced content such as images or videos fits its content box.
Use:
.avatar {
inline-size: 4rem;
aspect-ratio: 1;
object-fit: cover;
border-radius: 50%;
}
This makes a stable square crop without hard-coding both width and height.
Deep dive: overflow is a behavior decision
Common values:
.demo-a { overflow: visible; }
.demo-b { overflow: hidden; }
.demo-c { overflow: clip; }
.demo-d { overflow: auto; }
.demo-e { overflow-x: auto; overflow-y: hidden; }
Use auto when scrolling should appear only if needed. hidden clips and establishes scrolling behavior even if the user cannot directly scroll it in the usual way. clip clips without creating a scroll container.
Scrollable regions need intentional accessibility:
.table-wrap {
overflow-x: auto;
}
The content should remain keyboard/reflow friendly, and the user should not be trapped in nested scroll areas unnecessarily.
Worked example: long content destroys a flex layout
<div class="row">
<div class="content">
https://example.com/a-very-long-unbroken-identifier-that-cannot-wrap
</div>
<button>Copy</button>
</div>
.row {
display: flex;
gap: 1rem;
}
.content {
min-inline-size: 0;
overflow-wrap: anywhere;
}
Flex items have an automatic minimum size that can prevent them from shrinking. min-inline-size: 0 often solves the actual negotiation problem; overflow-wrap: anywhere gives the text a legal wrap opportunity.
Sizing failure lab
For any overflow bug, test these hypotheses in order:
- A child has an intrinsic minimum larger than the available space.
- A width plus padding/border exceeds the container.
- A
min-widthor fixed width is blocking shrinkage. - A transform visually moves content without changing layout size.
- A long word/URL has no break opportunity.
- A positioned child is escaping normal size calculations.
- The scroll container is the wrong element.
The goal is to identify which box owns the constraint.
Tiered exercises
Checkpoint: identify the overflow owner
Horizontal overflow can originate deep inside a page. Start at the document scrollbar, inspect the widest suspicious descendant, and move inward until one box crosses its containing block. Check intrinsic images, long tokens, white-space: nowrap, fixed widths, transforms, and 100vw. Repair the responsible component instead of clipping the document.
Distinguish acceptable overflow from failure. A code sample with a labeled local horizontal scroll area can preserve formatting. A button label, paragraph, or entire page extending off-screen is normally a failure. Keyboard-scroll the local region and confirm its focus indicator is visible; visual mouse scrolling alone is not enough evidence.
Test each constraint with content shorter and longer than expected. A max-inline-size protects readable measure on wide screens but does not force a narrow box to stay wide. A min-block-size provides visual presence but still yields to longer text. Write those relationships in plain language before selecting values; if the sentence says “always exactly,” reconsider whether real content can honor it.
Foundation: Make the portfolio wrapper fill available space, stop at 70rem, and center. Make all content images fluid while preserving aspect ratio.
Core: Replace fixed project card width/height with fluid constraints. Handle a long URL and a code block without hiding page overflow.
Stretch: Add one bounded fluid heading or section spacing value with clamp(). Explain its minimum, preferred expression, and maximum, then test at narrow width and 200% zoom.
.site-shell {
inline-size: 100%;
max-inline-size: 70rem;
margin-inline: auto;
padding-inline: 1rem;
}
img, svg, video { display: block; max-inline-size: 100%; block-size: auto; }
.project-card {
inline-size: 100%;
max-inline-size: 36rem;
min-block-size: 12rem;
padding: 1rem;
}
.resource-url { overflow-wrap: anywhere; }
pre { max-inline-size: 100%; overflow-x: auto; padding: 1rem; }
.hero { padding-block: clamp(2rem, 8vw, 6rem); }
.hero h1 { font-size: clamp(2rem, 1.5rem + 3vw, 4.5rem); }
The card has no fixed block size, so content can grow. Only the code block scrolls horizontally; the page remains within its viewport.
Recap and exit questions
Resilient sizing expresses relationships and limits rather than one rigid dimension. Let content determine block size, constrain inline measure, make media fluid, and fix the source of overflow instead of hiding it.
- When is
rempreferable topx? - Why can
100vwproduce page overflow? - What is the difference between
block-sizeandmin-block-size? - Why should code overflow locally while prose URLs wrap?
- What does each argument of
clamp()do?
