1. Fixed, fluid and responsive layout
There are three main approaches to markup:
| Approach | Units | Behavior |
|---|---|---|
| Fixed | Pixels (px) |
Sizes are hard-coded. When the window shrinks, a horizontal scrollbar appears |
| Fluid | Percentages (%) |
Elements resize proportionally together with the parent block, but stay in their places |
| Responsive | % + media queries |
At a certain point elements change their arrangement (for example, two columns become one) |
More than 60–80% of users of modern websites visit from mobile devices, so responsiveness is a mandatory requirement.
2. Width in percentages
Width in percentages is calculated from the width of the parent element.
/* Fixed layout */
.container { width: 1000px; }
.content { width: 650px; }
.sidebar { width: 350px; }
/* Fluid layout */
.content { width: 65%; }
.sidebar { width: 35%; }
A block element by default takes up 100% of the available width, so you don’t have to set width: on the parent container.
100%
When the browser window narrows, the width of the parent block decreases, and the child elements shrink proportionally along with it.
3. Height in percentages
Height in percentages works only when the parent element has an explicitly set height.
/* Doesn't work — the parent's height is auto */
.parent { }
.child { height: 50%; } /* ignored */
/* Works — the parent's height is set explicitly */
.parent { height: 400px; }
.child { height: 50%; } /* = 200px */
auto and is determined by the content, so the browser cannot compute a percentage of auto.Fixing the height for blocks with dynamic content is bad practice: if there is more content, it will overflow beyond the block’s boundaries.
4. Percentages for position: absolute and fixed
For absolutely positioned elements, percentage sizes are taken not from the direct parent, but from the nearest ancestor with position: relative (the positioning context).
.wrapper {
position: relative;
width: 500px;
height: 300px;
}
.parent {
width: 100px;
height: 100px;
}
.child {
position: absolute;
top: 0;
left: 0;
width: 100%; /* = 500px (from wrapper) */
height: 100%; /* = 300px (from wrapper) */
}
For position: fixed, sizes are always taken from the viewport — the browser window. width: 100% = the entire screen width, height: 100% = the entire screen height.
5. max-width and min-width
When the width is set in percentages or is unbounded, on wide screens the block stretches too much: text becomes hard to read, images get excessively cropped.
.container {
width: 100%;
max-width: 1280px;
margin-left: auto;
margin-right: auto;
}
| Property | What it does |
|---|---|
max-width |
Limits the maximum width. The block is “fluid” but will never become wider than the specified value |
min-width |
Limits the minimum width. If the screen is narrower, a horizontal scrollbar appears |
max-width, be sure to add margin-left:
auto and margin-right: auto to center the block. Otherwise it will be pushed against the left edge.min-width is a temporary solution: it does not eliminate horizontal scrolling. For correct display on narrow screens, you need a responsive layout with media queries.
6. margin-left: auto and margin-right: auto — the notation
To center a block, it’s better to write out the longhand properties instead of the shorthand notation:
/* Shorthand notation — sets unnecessary margin-top and margin-bottom */
.container { margin: 0 auto; }
/* Longhand notation — sets only what's needed */
.container {
margin-left: auto;
margin-right: auto;
}
The problem with the shorthand notation: margin: 0 auto zeroes out margin-top and margin-bottom. If you set vertical margins somewhere earlier in the CSS, they will be overwritten. With the longhand properties there is no such conflict.
7. margin and padding in percentages
A counterintuitive rule: both horizontal and vertical margin and padding in percentages are calculated from the width of the parent element.
.wrapper { width: 1000px; }
.card {
margin: 1%; /* top, right, bottom, left — all = 10px */
padding: 1%; /* top, right, bottom, left — all = 10px */
}
margin-top, margin-bottom, padding-top, padding-bottom in percentages are taken from the parent’s width, not from its height. This is standard CSS behavior.In practice, margins and padding are usually set in pixels, so this peculiarity rarely causes problems. But it underlies an interesting technique — preserving a block’s aspect ratio.
8. Aspect-ratio preservation technique (padding-hack)
Since padding-bottom in percentages is calculated from the width of the parent element, we can create a block that always preserves its aspect ratio:
.aspect-ratio-box {
height: 0;
padding-bottom: 100%; /* square */
overflow: hidden;
}
How it works:
height: 0— the content height does not affect the block’s sizepadding-bottom: 100%— the block’s height equals its width (because padding is calculated from the parent’s width)overflow: hidden— crops content that extends beyond the boundaries
The padding-bottom value for various aspect ratios:
| Aspect ratio | padding-bottom |
|---|---|
| 1:1 (square) | 100% |
| 16:9 (video) | 56.25% |
| 4:3 | 75% |
| 3:2 | 66.66% |
9. Embedding a YouTube video (a real-world example)
The most common use case for the padding-hack is creating a fluid container for an embedded video:
<!-- HTML -->
<div class="video-wrapper">
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
allowfullscreen></iframe>
</div>
/* CSS */
.video-wrapper {
position: relative;
height: 0;
padding-bottom: 56.25%; /* 16:9 */
overflow: hidden;
}
.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
How this works:
- The
iframewithposition: absolutedoes not affect the parent’s height - The height of
.video-wrapperis determined solely bypadding-bottom - The
iframestretches to 100% of the wrapper’s width and height - When the screen width changes, the video keeps its 16:9 aspect ratio
aspect-ratio property, which solves this task more elegantly: aspect-ratio: 16 / 9. It is supported by all modern browsers (Chrome 88+, Firefox 89+, Safari 15+). But the padding-hack is still found in older projects.10. font-size in percentages
The font size in percentages is calculated from the font-size of the parent element:
.parent { font-size: 20px; }
.child { font-size: 50%; } /* = 10px */
Practical example: the <button> element
The <button> element has a default font-size: 13.333px, which is not inherited from the parent. So that the button’s text size matches the container’s text size:
button {
font-size: 100%; /* inherits the size from the parent element */
}
This lets you change the font size in a single place (on the parent element), and it will automatically update in the button.
11. Flexbox: revisiting the key concepts
Two axes
Flexbox has a main and a cross axis. Instead of “left/right” it’s more correct to say “start/end of the axis”.
The height of items in a row
By default, flex items in a row stretch to the same height — this is the behavior of align-items:. Columns of equal height are the default Flexbox behavior; you don’t need to configure anything extra.
stretch
Changing the direction of the cross axis
The direction of the cross axis can be changed in two ways:
| Method | Explanation |
|---|---|
flex-direction: column |
The main axis becomes vertical → the cross axis becomes horizontal |
flex-wrap: wrap-reverse |
Items wrap in the reverse direction → the cross axis changes direction |
12. Flexbox instead of float
Using float to create columns is an outdated approach. All modern browsers fully support Flexbox.
Flexbox automatically cancels float
.news-item {
float: left; /* the old approach for columns */
}
.news {
display: flex; /* float automatically stops working */
}
If display: flex is set on the parent element, then the float of its child elements is ignored. This is built into the Flexbox specification.
Float problems that Flexbox solves
- The parent block “loses” its height (a clearfix hack is needed)
- It’s harder to control alignment and the distribution of space
- Extra hacks are needed for equal-height columns
float used for columns, feel free to replace it with display: flex. Keep float only for its original purpose — wrapping text around images.13. Clearfix — the float hack (historical note)
When float was used for columns, the parent block would “collapse” — it did not account for the height of the float elements. To fix this, clearfix was applied:
.clearfix::after {
content: "";
display: table;
clear: both;
}
In modern markup with Flexbox, clearfix is not needed — it’s a completely obsolete technique.
14. Summary table: percentages in CSS
| Property | What the % is calculated from | Note |
|---|---|---|
width |
The width of the parent element | Always works |
height |
The height of the parent element | Only if the parent’s height is set explicitly |
margin (all sides) |
The width of the parent element | Even vertical ones — from the width! |
padding (all sides) |
The width of the parent element | Even vertical ones — from the width! |
font-size |
The font-size of the parent element |
There are better units (em, rem) |
width / height with position: absolute |
The size of the nearest ancestor with position: relative |
Not from the direct parent! |
width / height with position: fixed |
The size of the viewport (the browser window) | Always from the window |