← HTML & CSS/Flexbox: выравнивание и wrap#25 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

Flexbox: выравнивание и wrap

В прошлом уроке мы научились строить простые строки. Теперь разберём мощные инструменты: многострочные лейауты, контроль порядка элементов и тонкую настройку распределения пространства. Это то, что позволяет строить реальные интерфейсы — грид карточек, адаптивные лейауты.

align-content — выравнивание нескольких строк

Работает только когда flex-wrap: wrap и строк больше одной. Управляет распределением строк по поперечной оси.

align-content: flex-start;    /* Строки к началу */
align-content: flex-end;      /* Строки к концу */
align-content: center;        /* Строки по центру */
align-content: space-between; /* Первая строка вверху, последняя внизу */
align-content: space-around;  /* Равные отступы вокруг строк */
align-content: stretch;       /* Строки растягиваются (по умолчанию) */

align-items — выравнивает элементы внутри одной строки.

align-content — выравнивает строки внутри контейнера.

order — изменение порядка

Позволяет переставить элементы без изменения HTML. По умолчанию всё имеет order: 0.

.btn-primary { order: -1; }  /* Первый, хотя в HTML идёт последним */
.sidebar { order: 2; }       /* Последний, хотя в HTML идёт первым */

Применение: на мобильных вынести важный контент наверх, не меняя HTML.

@media (max-width: 768px) {
  .main-content { order: 1; }
  .sidebar { order: 2; }
}

flex-grow — распределение свободного места

Если в контейнере есть свободное место, flex-grow определяет, как его поделить.

/* Лейаут: сайдбар + контент */
.sidebar { flex-grow: 1; }   /* 1 часть свободного места */
.main    { flex-grow: 3; }   /* 3 части свободного места */
/* Итог: sidebar = 25%, main = 75% */

По умолчанию flex-grow: 0 — элемент не растёт.

flex-shrink — сжатие при нехватке места

Определяет, как элемент уменьшается, когда места не хватает.

.logo   { flex-shrink: 0; }  /* Не сжимается никогда */
.search { flex-shrink: 1; }  /* Сжимается (по умолчанию) */

По умолчанию flex-shrink: 1 — все элементы сжимаются равномерно.

flex-basis — начальный размер

Задаёт исходный размер перед применением grow/shrink.

.card { flex-basis: 200px; }  /* Начинает с 200px, потом растёт/сжимается */
.card { flex-basis: 30%; }    /* Начинает с 30% от контейнера */
.card { flex-basis: auto; }   /* Берёт размер из width/height (по умолчанию) */
.card { flex-basis: 0; }      /* Начинает с нуля, растёт только через flex-grow */

flex — сокращение

flex: <grow> <shrink> <basis>

flex: 1;          /* = flex: 1 1 0  — растёт, сжимается, начинает с 0 */
flex: auto;       /* = flex: 1 1 auto */
flex: none;       /* = flex: 0 0 auto — не растёт, не сжимается */
flex: 0 0 200px;  /* Фиксированный размер 200px, не меняется */

align-self — выравнивание одного элемента

Переопределяет align-items для конкретного дочернего элемента.

.container { align-items: center; }
.special { align-self: flex-start; }  /* Только этот элемент прижат к верху */

Значения те же: flex-start, flex-end, center, stretch, baseline.

Практический пример: адаптивный грид карточек

/* Родитель */
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* Карточка */
.card {
  flex: 1 1 280px;  /* grow=1, shrink=1, basis=280px */
  /* На широком экране: несколько в ряд */
  /* На мобильном: каждая занимает всю строку */
}

Это минимальный адаптивный грид без media queries!

Типичные ошибки

Ошибка 1: Перепутать align-items и align-content

/* Одна строка — align-items работает, align-content нет */
.single-row { display: flex; align-items: center; }  /* OK */
.single-row { display: flex; align-content: center; }  /* Нет эффекта */

/* Несколько строк (flex-wrap: wrap) — нужен align-content */
.multi-row { display: flex; flex-wrap: wrap; align-content: center; }

Ошибка 2: flex: 1 и фиксированный размер одновременно

.item { flex: 1; width: 200px; }  /* width игнорируется! Используй flex-basis */
.item { flex: 0 0 200px; }        /* Правильный фиксированный размер */

В реальных проектах

Паттерн flex: 1 1 280px с flex-wrap: wrap — основа адаптивных карточных лейаутов на всех крупных интернет-магазинах. Instagram-подобная лента из трёх карточек в ряд на десктопе и одной на мобиле строится именно так, часто без единого media query.

Примеры

flex-grow, flex-shrink, align-self и order — продвинутый Flexbox

const style = document.createElement('style')
style.textContent = `
  * { box-sizing: border-box; }
  body { font-family: Arial, sans-serif; padding: 16px; background: #f7fafc; }
`
document.head.appendChild(style)

// === Лейаут sidebar + main через flex-grow ===
const layout = document.createElement('div')
layout.style.display = 'flex'
layout.style.gap = '16px'
layout.style.marginBottom = '24px'
layout.style.height = '120px'
document.body.appendChild(layout)

const sidebar = document.createElement('aside')
sidebar.style.flexGrow = '1'
sidebar.style.backgroundColor = '#bee3f8'
sidebar.style.padding = '16px'
sidebar.style.borderRadius = '8px'
sidebar.textContent = 'Sidebar (flex-grow: 1)'

const main = document.createElement('main')
main.style.flexGrow = '3'
main.style.backgroundColor = '#c6f6d5'
main.style.padding = '16px'
main.style.borderRadius = '8px'
main.textContent = 'Main (flex-grow: 3 = 75% пространства)'

layout.appendChild(sidebar)
layout.appendChild(main)

// === align-self: каждый элемент выравнивается по-своему ===
const row = document.createElement('div')
row.style.display = 'flex'
row.style.alignItems = 'flex-start'  // Default для всех
row.style.gap = '8px'
row.style.height = '100px'
row.style.backgroundColor = '#fff'
row.style.padding = '8px'
row.style.borderRadius = '8px'
row.style.marginBottom = '16px'
document.body.appendChild(row)

const alignments = [
  { text: 'flex-start', alignSelf: 'flex-start', bg: '#fed7d7' },
  { text: 'center', alignSelf: 'center', bg: '#fbb6ce' },
  { text: 'flex-end', alignSelf: 'flex-end', bg: '#d6bcfa' },
  { text: 'stretch', alignSelf: 'stretch', bg: '#bee3f8' },
]

alignments.forEach(({ text, alignSelf, bg }) => {
  const el = document.createElement('div')
  el.style.flex = '1'
  el.style.alignSelf = alignSelf
  el.style.backgroundColor = bg
  el.style.padding = '8px'
  el.style.borderRadius = '4px'
  el.style.fontSize = '12px'
  el.textContent = alignSelf
  row.appendChild(el)
})

// === order: изменяем порядок ===
const orderContainer = document.createElement('div')
orderContainer.style.display = 'flex'
orderContainer.style.gap = '8px'
document.body.appendChild(orderContainer)

;[
  { text: 'Третий в HTML', order: 3, bg: '#fed7d7' },
  { text: 'Первый в HTML', order: 1, bg: '#c6f6d5' },
  { text: 'Второй в HTML', order: 2, bg: '#bee3f8' },
].forEach(({ text, order, bg }) => {
  const el = document.createElement('div')
  el.style.order = String(order)
  el.style.flex = '1'
  el.style.padding = '12px'
  el.style.backgroundColor = bg
  el.style.borderRadius = '8px'
  el.style.textAlign = 'center'
  el.style.fontSize = '13px'
  el.textContent = text + ' (order: ' + order + ')'
  orderContainer.appendChild(el)
})

// Лог
const sidebarStyle = window.getComputedStyle(sidebar)
const mainStyle = window.getComputedStyle(main)
console.log('Sidebar flex-grow:', sidebarStyle.flexGrow)   // 1
console.log('Main flex-grow:', mainStyle.flexGrow)         // 3

// Ширины — видим что main занимает ~75%
console.log('Sidebar ширина:', sidebar.offsetWidth + 'px')
console.log('Main ширина:', main.offsetWidth + 'px')

Flexbox: выравнивание и wrap

В прошлом уроке мы научились строить простые строки. Теперь разберём мощные инструменты: многострочные лейауты, контроль порядка элементов и тонкую настройку распределения пространства. Это то, что позволяет строить реальные интерфейсы — грид карточек, адаптивные лейауты.

align-content — выравнивание нескольких строк

Работает только когда flex-wrap: wrap и строк больше одной. Управляет распределением строк по поперечной оси.

align-content: flex-start;    /* Строки к началу */
align-content: flex-end;      /* Строки к концу */
align-content: center;        /* Строки по центру */
align-content: space-between; /* Первая строка вверху, последняя внизу */
align-content: space-around;  /* Равные отступы вокруг строк */
align-content: stretch;       /* Строки растягиваются (по умолчанию) */

align-items — выравнивает элементы внутри одной строки.

align-content — выравнивает строки внутри контейнера.

order — изменение порядка

Позволяет переставить элементы без изменения HTML. По умолчанию всё имеет order: 0.

.btn-primary { order: -1; }  /* Первый, хотя в HTML идёт последним */
.sidebar { order: 2; }       /* Последний, хотя в HTML идёт первым */

Применение: на мобильных вынести важный контент наверх, не меняя HTML.

@media (max-width: 768px) {
  .main-content { order: 1; }
  .sidebar { order: 2; }
}

flex-grow — распределение свободного места

Если в контейнере есть свободное место, flex-grow определяет, как его поделить.

/* Лейаут: сайдбар + контент */
.sidebar { flex-grow: 1; }   /* 1 часть свободного места */
.main    { flex-grow: 3; }   /* 3 части свободного места */
/* Итог: sidebar = 25%, main = 75% */

По умолчанию flex-grow: 0 — элемент не растёт.

flex-shrink — сжатие при нехватке места

Определяет, как элемент уменьшается, когда места не хватает.

.logo   { flex-shrink: 0; }  /* Не сжимается никогда */
.search { flex-shrink: 1; }  /* Сжимается (по умолчанию) */

По умолчанию flex-shrink: 1 — все элементы сжимаются равномерно.

flex-basis — начальный размер

Задаёт исходный размер перед применением grow/shrink.

.card { flex-basis: 200px; }  /* Начинает с 200px, потом растёт/сжимается */
.card { flex-basis: 30%; }    /* Начинает с 30% от контейнера */
.card { flex-basis: auto; }   /* Берёт размер из width/height (по умолчанию) */
.card { flex-basis: 0; }      /* Начинает с нуля, растёт только через flex-grow */

flex — сокращение

flex: <grow> <shrink> <basis>

flex: 1;          /* = flex: 1 1 0  — растёт, сжимается, начинает с 0 */
flex: auto;       /* = flex: 1 1 auto */
flex: none;       /* = flex: 0 0 auto — не растёт, не сжимается */
flex: 0 0 200px;  /* Фиксированный размер 200px, не меняется */

align-self — выравнивание одного элемента

Переопределяет align-items для конкретного дочернего элемента.

.container { align-items: center; }
.special { align-self: flex-start; }  /* Только этот элемент прижат к верху */

Значения те же: flex-start, flex-end, center, stretch, baseline.

Практический пример: адаптивный грид карточек

/* Родитель */
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* Карточка */
.card {
  flex: 1 1 280px;  /* grow=1, shrink=1, basis=280px */
  /* На широком экране: несколько в ряд */
  /* На мобильном: каждая занимает всю строку */
}

Это минимальный адаптивный грид без media queries!

Типичные ошибки

Ошибка 1: Перепутать align-items и align-content

/* Одна строка — align-items работает, align-content нет */
.single-row { display: flex; align-items: center; }  /* OK */
.single-row { display: flex; align-content: center; }  /* Нет эффекта */

/* Несколько строк (flex-wrap: wrap) — нужен align-content */
.multi-row { display: flex; flex-wrap: wrap; align-content: center; }

Ошибка 2: flex: 1 и фиксированный размер одновременно

.item { flex: 1; width: 200px; }  /* width игнорируется! Используй flex-basis */
.item { flex: 0 0 200px; }        /* Правильный фиксированный размер */

В реальных проектах

Паттерн flex: 1 1 280px с flex-wrap: wrap — основа адаптивных карточных лейаутов на всех крупных интернет-магазинах. Instagram-подобная лента из трёх карточек в ряд на десктопе и одной на мобиле строится именно так, часто без единого media query.

Примеры

flex-grow, flex-shrink, align-self и order — продвинутый Flexbox

const style = document.createElement('style')
style.textContent = `
  * { box-sizing: border-box; }
  body { font-family: Arial, sans-serif; padding: 16px; background: #f7fafc; }
`
document.head.appendChild(style)

// === Лейаут sidebar + main через flex-grow ===
const layout = document.createElement('div')
layout.style.display = 'flex'
layout.style.gap = '16px'
layout.style.marginBottom = '24px'
layout.style.height = '120px'
document.body.appendChild(layout)

const sidebar = document.createElement('aside')
sidebar.style.flexGrow = '1'
sidebar.style.backgroundColor = '#bee3f8'
sidebar.style.padding = '16px'
sidebar.style.borderRadius = '8px'
sidebar.textContent = 'Sidebar (flex-grow: 1)'

const main = document.createElement('main')
main.style.flexGrow = '3'
main.style.backgroundColor = '#c6f6d5'
main.style.padding = '16px'
main.style.borderRadius = '8px'
main.textContent = 'Main (flex-grow: 3 = 75% пространства)'

layout.appendChild(sidebar)
layout.appendChild(main)

// === align-self: каждый элемент выравнивается по-своему ===
const row = document.createElement('div')
row.style.display = 'flex'
row.style.alignItems = 'flex-start'  // Default для всех
row.style.gap = '8px'
row.style.height = '100px'
row.style.backgroundColor = '#fff'
row.style.padding = '8px'
row.style.borderRadius = '8px'
row.style.marginBottom = '16px'
document.body.appendChild(row)

const alignments = [
  { text: 'flex-start', alignSelf: 'flex-start', bg: '#fed7d7' },
  { text: 'center', alignSelf: 'center', bg: '#fbb6ce' },
  { text: 'flex-end', alignSelf: 'flex-end', bg: '#d6bcfa' },
  { text: 'stretch', alignSelf: 'stretch', bg: '#bee3f8' },
]

alignments.forEach(({ text, alignSelf, bg }) => {
  const el = document.createElement('div')
  el.style.flex = '1'
  el.style.alignSelf = alignSelf
  el.style.backgroundColor = bg
  el.style.padding = '8px'
  el.style.borderRadius = '4px'
  el.style.fontSize = '12px'
  el.textContent = alignSelf
  row.appendChild(el)
})

// === order: изменяем порядок ===
const orderContainer = document.createElement('div')
orderContainer.style.display = 'flex'
orderContainer.style.gap = '8px'
document.body.appendChild(orderContainer)

;[
  { text: 'Третий в HTML', order: 3, bg: '#fed7d7' },
  { text: 'Первый в HTML', order: 1, bg: '#c6f6d5' },
  { text: 'Второй в HTML', order: 2, bg: '#bee3f8' },
].forEach(({ text, order, bg }) => {
  const el = document.createElement('div')
  el.style.order = String(order)
  el.style.flex = '1'
  el.style.padding = '12px'
  el.style.backgroundColor = bg
  el.style.borderRadius = '8px'
  el.style.textAlign = 'center'
  el.style.fontSize = '13px'
  el.textContent = text + ' (order: ' + order + ')'
  orderContainer.appendChild(el)
})

// Лог
const sidebarStyle = window.getComputedStyle(sidebar)
const mainStyle = window.getComputedStyle(main)
console.log('Sidebar flex-grow:', sidebarStyle.flexGrow)   // 1
console.log('Main flex-grow:', mainStyle.flexGrow)         // 3

// Ширины — видим что main занимает ~75%
console.log('Sidebar ширина:', sidebar.offsetWidth + 'px')
console.log('Main ширина:', main.offsetWidth + 'px')

Задание

Создай шапку сайта с тремя блоками во flex-контейнере высотой 70px. Первый блок — логотип с `flex-shrink: 0` и `flex-basis: 80px` (не сжимается). Второй блок — контент с `flex-grow: 1` (занимает всё свободное место). Третий блок — бейдж с `align-self: flex-end` (прижат к нижней части контейнера).

Подсказка

`flex-shrink: 0` — логотип не сжимается при нехватке места. `flex-basis: 80px` — начальная ширина. `flex-grow: 1` — контент занимает всё свободное пространство. `align-self: flex-end` — бейдж прижат к нижней части контейнера.

Загружаем среду выполнения...
Загружаем AI-помощника...