Flexbox — the Basics
Flexbox (Flexible Box Layout) is a tool for arranging elements in a row or column. It lets you flexibly control the layout without hacks.
How to enable it?
display: flex is set on the parent element (the flex container). After that, its child elements become flex items.
.header {
display: flex;
/* Child elements are arranged horizontally */
}
The two-axis principle
Flexbox has two axes:
- Main axis — the direction along which flex items are placed. By default: left to right.
- Cross axis — perpendicular to the main axis. By default: top to bottom.
Aligning by height
By default, flex items in the same row are aligned to the height of the tallest element. This is very handy for cards of equal height.
flex-direction — the direction of the main axis
Controls the direction of the main axis (and, accordingly, the placement of flex items).
| Value | Main axis | Result |
|---|---|---|
row (default) |
→ left to right | Items in a row |
row-reverse |
← right to left | Items in a row, in reverse order |
column |
↓ top to bottom | Items in a column |
column-reverse |
↑ bottom to top | Items in a column, in reverse order |
flex-wrap — wrapping items
By default, flex items do not wrap onto a new line — they shrink instead.
| Value | Behavior |
|---|---|
nowrap (default) |
Items shrink, they do not wrap |
wrap |
Items wrap onto a new line if they do not fit |
wrap-reverse |
Wrapping in the reverse direction of the cross axis |
wrap it moves to a new line. With nowrap, all four shrink.Practical example: a site header
A typical task: logo on the left, menu on the right — horizontally.
<!-- HTML -->
<header class="header">
<a href="/" class="logo">
<img src="logo.svg" alt="Logo">
</a>
<nav class="menu">
<ul class="menu-list">
<li class="menu-list-item">
<a href="#">Home</a>
</li>
<li class="menu-list-item">
<a href="#">About us</a>
</li>
</ul>
</nav>
</header>
/* CSS */
.header {
display: flex; /* logo and nav — in a row */
}
.menu-list {
display: flex; /* menu items — in a row */
margin: 0;
padding: 0;
list-style-type: none;
}
display: flex is set on the parent of the elements you want to arrange. If the container has only one child element, Flexbox will not change anything.The <header>, <nav>, <footer> tags
<header> — the header
- Introductory, supporting information before the main content.
- It can be the header of the site (in
<body>), an article (in<article>), or a section (in<section>). - A page may have several
<header>elements in different contexts.
<nav> — navigation
- A block of the site’s main navigation or breadcrumbs.
- The main menu is an unordered list
<ul>(order does not matter). - Breadcrumbs are an ordered list
<ol>(order matters).
<footer> — the footer
- Supporting information after the main content.
- Like
<header>, it can be the footer of the site, an article, or a section. - Typical content: copyright, contacts, links to social media, publication date.
<div> with a semantic tag, check whether the browser has added default styles (the User Agent Stylesheet). For <header> and <footer> it is usually just display: block, so there are no problems.Styling a navigation list
When using <ul> for a menu, you need to reset the default styles:
.menu-list {
margin: 0;
padding: 0;
list-style-type: none; /* remove the markers */
}
list-style-type) are needed for lists within article text. In navigation, cards, and tabs — always remove them.Margin collapsing
The vertical margin-top and margin-bottom of block elements have special behavior:
1. Collapsing between siblings
When the margin-bottom of one element meets the margin-top of the next, they are not added together — the larger one is used.
margin-bottom: 20px, a paragraph has margin-top: 10px. The distance between them = 20px (the larger), not 30px.2. Margin escaping the parent
The margin-top of the first child and the margin-bottom of the last child can “leak” outside the parent element.
How to prevent leaking?
The margin stays inside the parent if the parent has:
padding(at leastpadding-top: 1px)border(at leastborder-top: 1px solid transparent)display: flexordisplay: gridoverflow(with a value other thanvisible)
padding or border to the parent, the margin behavior may suddenly change. This is a common cause of “unexplained” shifts in a layout.The :nth-child() and :nth-of-type() pseudo-classes
Pseudo-classes let you select elements by their position among their siblings. Syntax: selector:pseudo-class (separated by a colon).
The difference between nth-child and nth-of-type
| Pseudo-class | Counts |
|---|---|
:nth-child() |
All child elements regardless of tag type |
:nth-of-type() |
Each tag type separately (<div> has its own count, <article> has its own) |
Values
| Value | What it selects | Example |
|---|---|---|
odd |
Odd: 1, 3, 5, 7… | .card:nth-child(odd) |
even |
Even: 2, 4, 6, 8… | .card:nth-child(even) |
a number (e.g. 3) |
A specific element | .card:nth-child(3) |
the formula an+b |
Every a-th, starting from the b-th | .card:nth-child(2n+3) |
The an+b formula — how it works
- a — the step (every a-th element)
- n — the variable (the browser substitutes 0, 1, 2, 3…)
- b — the offset (which element to start from)
2n+3: starting from the 3rd, every 2nd.n=0 → 3, n=1 → 5, n=2 → 7, n=3 → 9…
Result: the 3rd, 5th, 7th, 9th elements.
Common combinations
| Formula | Equivalent | Selects |
|---|---|---|
2n |
even |
Every even one |
2n+1 |
odd |
Every odd one |
n+2 |
— | All, starting from the 2nd (i.e. all except the first) |
3n |
— | Every 3rd: 3, 6, 9… |
-n+3 |
— | The first 3 elements |
Shorthand pseudo-classes
| Pseudo-class | Equivalent | Selects |
|---|---|---|
:first-child |
:nth-child(1) |
The first element |
:last-child |
— | The last element |
:first-of-type |
:nth-of-type(1) |
The first element of each type |
:last-of-type |
— | The last element of each type |
Practice: spacing between cards
A typical task: a list of cards with spacing between them, but without extra spacing at the top.
A solution with nth-child
/* Spacing for all cards, starting from the second */
.post:nth-child(n+2) {
margin-top: 35px;
}
An alternative solution
/* Give all of them a margin-top */
.post {
margin-top: 35px;
}
/* Remove it from the first one */
.post:first-child {
margin-top: 0;
}
Zebra striping (every even card a different color)
.post:nth-child(even) {
background-color: #1a1a1a;
color: #fff;
}
The modern approach: gap
The gap property sets the spacing between flex items without needing pseudo-classes and margin hacks:
.cards {
display: flex;
flex-direction: column;
gap: 35px; /* spacing between cards, without extra top/bottom */
}
gap has been fully supported in Flexbox since 2021 (all modern browsers). It is the cleanest way to set spacing between elements — gap creates no extra margin at either the top or the bottom.The ::before and ::after pseudo-elements
Pseudo-elements create virtual elements in the DOM for styling, without cluttering the HTML.
::before— inserts content before the element’s content.::after— inserts content after the element’s content.- The
contentproperty is required.
Common uses
- Asterisks for required form fields
- Custom list markers
- Decorative arrows and icons
- Background overlays (overlay)
/* Asterisk for a required field */
.required::after {
content: " *";
color: red;
}
::before), but they also work with one (:before). Two are recommended to distinguish them from pseudo-classes.Normalize CSS vs Reset CSS
Default styles differ between browsers. There are two approaches to unifying them:
- Brings styles to a uniform appearance
- Keeps useful default styles
- Suitable for most sites
- The recommended approach
- Completely resets all styles
- You need to set everything from scratch
- Suitable for web apps and games
- Used less often
* { margin: 0; padding: 0; } as a replacement for Normalize — it resets the useful spacing in articles, lists, and headings, and you will have to add it back manually.CSS Grid — two-dimensional layout
CSS Grid Layout is a tool for creating two-dimensional layouts (rows and columns at the same time). It is fully supported by all modern browsers.
Flexbox vs Grid
- One-dimensional (a row or a column)
- The items determine the size
- Ideal for components
- Header, menu, card, form
- Two-dimensional (rows and columns)
- The container determines the layout
- Ideal for pages
- A grid of cards, a page layout
How to enable it?
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 3 equal columns */
gap: 20px; /* spacing between items */
}
display: grid is set on the parent element (the grid container). Child elements are automatically placed in the grid.
Grid: core properties
grid-template-columns — columns
Defines the number and width of columns:
/* Three columns of 200px each */
grid-template-columns: 200px 200px 200px;
/* Three equal columns (flexible) */
grid-template-columns: 1fr 1fr 1fr;
/* Shorthand: 3 columns of 1fr */
grid-template-columns: repeat(3, 1fr);
/* Sidebar 250px, main content — the rest */
grid-template-columns: 250px 1fr;
fr (fraction) unit is a share of the free space. 1fr 2fr means: the first column gets 1/3, the second gets 2/3 of the available width.grid-template-rows — rows
/* Header 80px, content flexible, footer 60px */
grid-template-rows: 80px 1fr 60px;
gap — spacing
gap: 20px; /* equal spacing */
gap: 20px 30px; /* rows: 20px, columns: 30px */
row-gap: 20px; /* only between rows */
column-gap: 30px; /* only between columns */
Grid: placing elements
Automatic placement
By default, elements fill the grid in order, left to right, row by row.
Manual placement
You can specify which rows and columns an element occupies:
.hero {
grid-column: 1 / 3; /* from line 1 to line 3 (2 columns) */
grid-row: 1 / 2; /* the first row */
}
.sidebar {
grid-column: 3; /* the third column */
grid-row: 1 / 3; /* from row 1 to row 3 */
}
span shorthand: grid-column: span 2 means “occupy 2 columns from the current position”.Grid: practical examples
A grid of cards (responsive)
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
auto-fill + minmax() — the cards automatically wrap onto a new line when the screen width decreases. Without media queries!
A classic page layout
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.page-header {
grid-column: 1 / -1; /* full width */
}
.page-footer {
grid-column: 1 / -1; /* full width */
}
When to use what
| Task | Tool | Why |
|---|---|---|
| Header: logo + menu | Flexbox | One row, two elements |
| Menu items in a row | Flexbox | A one-dimensional list |
| A 3×N grid of cards | Grid | A two-dimensional grid |
| Page layout (header + sidebar + content + footer) | Grid | A complex two-dimensional structure |
| Centering a single element | Flexbox or Grid | Both work |
| Buttons in a row with spacing | Flexbox + gap | A simple row with gap |
Summary
| Topic | Key point |
|---|---|
| Flexbox | display: flex on the parent → children in a row |
flex-direction |
row | column | row-reverse | column-reverse |
flex-wrap |
nowrap (shrinks) | wrap (wraps) |
<header> |
Header of the site / section / article |
<nav> |
Main navigation or breadcrumbs |
<footer> |
Footer of the site / section / article |
| Margin collapsing | Vertical margins collapse (the larger one is used) |
:nth-child() |
Selection by position: odd, even, an+b |
::before / ::after |
Virtual elements for styling (content required) |
| Normalize CSS | Unifies styles between browsers (recommended) |
gap |
Spacing between flex/grid items without margin hacks |
| CSS Grid | display: grid — two-dimensional layout (rows + columns) |
grid-template-columns |
repeat(3, 1fr), minmax(), auto-fill |
| Flexbox vs Grid | Flex — components (1D); Grid — pages (2D). Use both |