Positioning Block Elements

Lecture notes: Flexbox, CSS Grid, semantic header/nav/footer tags, margin collapsing, pseudo-classes

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 */
}
A flex container behaves like a block element (it takes up the full width). The only difference is that special properties become available for controlling the child elements.

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.
Avoid the terms “left”, “right”, “up”, “down” when working with Flexbox. The axes can be reversed, so think in terms of “start/end of the main axis” and “start/end of the cross axis”.

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
Three cards fit in a row. The fourth does not fit → with 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.
When you replace a <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 */
}
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.

A heading has 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 least padding-top: 1px)
  • border (at least border-top: 1px solid transparent)
  • display: flex or display: grid
  • overflow (with a value other than visible)
Be careful: if you later add 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)
Numbering starts from 1 (not from 0, as in programming).

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)
The formula 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;
}
Zebra striping is often used in tables too — for better readability of long lists.

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 content property 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;
}
Pseudo-elements are written with two colons (::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:

Normalize CSS

  • Brings styles to a uniform appearance
  • Keeps useful default styles
  • Suitable for most sites
  • The recommended approach
Reset CSS

  • Completely resets all styles
  • You need to set everything from scratch
  • Suitable for web apps and games
  • Used less often
Do not use * { 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

Flexbox

  • One-dimensional (a row or a column)
  • The items determine the size
  • Ideal for components
  • Header, menu, card, form
Grid

  • Two-dimensional (rows and columns)
  • The container determines the layout
  • Ideal for pages
  • A grid of cards, a page layout
Flexbox and Grid are not competitors but partners. Grid is for the overall page structure, Flexbox is for the components inside. Use both.

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;
The 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 */
}
The 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 */
}
The result: a full-width header, a sidebar (250px) + main content (the rest), and a full-width footer. All of it — without Flexbox hacks.

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

Vitalii Kaplia

Founder, Web Developer & WordPress Expert

I became interested in programming back in 1997. The first acquaintance with a future profession was using Visual Basic. In…

More about author

A digital product engineer and web solutions architect

Free consultation + cost calculation

Let’s discuss your project?

Free consultation + cost calculation

More interesting articles

Customer login

This site uses cookies

We use cookies to personalize content and ads, provide social media features, and analyze our traffic. We also share information about your use of our website with our social media, advertising, and analytics partners, who may combine it with other information you have provided to them or collected when you use their services. By continuing to use our site, you consent to our use of cookies and accept our Privacy Policy and Terms of Use.

Virtual assistant
Hi! I'm the KAPLIA.PRO virtual assistant! I can tell you about our services, portfolio projects, ballpark pricing, and how we work — from websites and eCommerce to web apps, CRMs, bots, and AI agents. Ask away!

By continuing with the AI assistant, I agree to the site’s terms of use and privacy policy.

AI assistant may make mistakes in responses