Урок 5 из 49 · Месяц 1. HTML5 и CSS3

Flexbox и псевдокласс :hover

Содержание урока

Flexbox — это способ раскладывать элементы в ряд или в колонку, выравнивать их по центру и распределять между ними место буквально парой строк CSS. В этом уроке разберём, как работает flex-контейнер, научимся строить меню, карточки в ряд и шапку сайта, а затем оживим страницу псевдоклассом :hover, который меняет стиль при наведении мыши. В конце — практика, которую на занятии дополнит ментор.

Зачем нужен Flexbox

Вспомните прошлый урок: блочные элементы всегда идут друг под другом. А что если нужно поставить три карточки в ряд, разместить логотип слева, а меню справа, или выровнять кнопку точно по центру экрана? Раньше для этого использовали хитрые приёмы, которые легко ломались. Flexbox (Flexible Box — «гибкая коробка») решает такие задачи просто и надёжно.

Контейнер и элементы

В Flexbox всегда участвуют двое:

  • flex-контейнер — родитель, которому написали display: flex;
  • flex-элементы — его прямые дети.
<div class="row">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
</div>
.row {
  display: flex;
}

.item {
  padding: 20px;
  background-color: lightblue;
  border: 1px solid steelblue;
}

Одна строка display: flex — и три блока встали в ряд, а не друг под другом. Каждый занимает столько места, сколько нужно его содержимому.

Главная и поперечная оси

Flexbox мыслит не «горизонталью» и «вертикалью», а двумя осями:

  • главная ось (main axis) — вдоль неё выстраиваются элементы;
  • поперечная ось (cross axis) — перпендикулярна главной.
flex-direction: row (по умолчанию)

  главная ось →
 ┌─────┬─────┬─────┐  ↓
 │  1  │  2  │  3  │  поперечная ось
 └─────┴─────┴─────┘

Направление главной оси задаёт flex-direction:

Значение Главная ось
row Слева направо (по умолчанию)
row-reverse Справа налево
column Сверху вниз
column-reverse Снизу вверх

Это важно, потому что следующие свойства работают вдоль осей, а не вдоль сторон экрана.

Свойства контейнера

justify-content — выравнивание по главной оси

.row {
  display: flex;
  justify-content: space-between;
}
Значение Что делает
flex-start Прижать к началу (по умолчанию)
flex-end Прижать к концу
center По центру
space-between Крайние — к краям, между остальными равные промежутки
space-around Равные отступы вокруг каждого элемента
space-evenly Совершенно равные промежутки, включая края

align-items — выравнивание по поперечной оси

.row {
  display: flex;
  height: 200px;
  align-items: center;
}
Значение Что делает
stretch Растянуть на всю высоту контейнера (по умолчанию)
flex-start Прижать к началу поперечной оси (вверх)
flex-end Прижать к концу (вниз)
center По центру
baseline Выровнять по базовой линии текста

Именно из-за stretch по умолчанию карточки в одном ряду получаются одинаковой высоты, даже если текста в них разное количество.

Центрирование — классика

.screen {
  display: flex;
  justify-content: center;   /* по горизонтали */
  align-items: center;       /* по вертикали */
  min-height: 100vh;         /* на весь экран */
}

Три строки — и любой элемент внутри .screen стоит ровно посередине экрана. Это один из самых частых приёмов во frontend.

gap — промежутки между элементами

.row {
  display: flex;
  gap: 16px;          /* одинаковый промежуток между всеми элементами */
}

.grid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px 16px;     /* между рядами 24px, между колонками 16px */
}

gap удобнее, чем margin: промежуток появляется только между элементами, без лишнего отступа у крайних.

flex-wrap — перенос на новую строку

По умолчанию (nowrap) flex-элементы стараются уместиться в одну строку и сжимаются, даже если места мало. С flex-wrap: wrap они переносятся на следующую строку:

.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

.card {
  width: 280px;
}

Если окно узкое, карточки будут ложиться в два ряда, потом в один.

Свойства элементов

flex-grow, flex-shrink, flex-basis

.sidebar { flex-basis: 250px; flex-shrink: 0; }
.content { flex-grow: 1; }
  • flex-basis — начальный размер элемента вдоль главной оси (как width для ряда).
  • flex-grow — какую долю свободного места забрать. 1 — «забери всё, что осталось». Если у двух элементов 1 и 2, второй получит вдвое больше свободного места.
  • flex-shrink — можно ли элементу сжиматься, если места не хватает. 0 — нельзя.

Обычно пишут сокращение flex:

.content { flex: 1; }          /* растягивается и занимает остаток */
.col     { flex: 1 1 200px; }  /* grow, shrink, basis */

align-self и order

.special {
  align-self: flex-end;  /* выровнять только этот элемент иначе, чем остальных */
}

.first {
  order: -1;             /* показать первым, хотя в HTML он не первый */
}

order меняет только визуальный порядок. Для программ чтения с экрана порядок остаётся как в HTML, поэтому злоупотреблять им не стоит.

Пример: шапка сайта

<header class="header">
    <a class="logo" href="#">Atlas</a>
    <nav class="nav">
        <a href="#">Главная</a>
        <a href="#">Курсы</a>
        <a href="#">Контакты</a>
    </nav>
    <a class="btn" href="#">Войти</a>
</header>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 32px;
  background-color: #1a202c;
}

.logo {
  font-size: 1.5rem;
  font-weight: 700;
  color: white;
  text-decoration: none;
}

.nav {
  display: flex;
  gap: 24px;
}

.nav a {
  color: #cbd5e0;
  text-decoration: none;
}

.btn {
  padding: 8px 20px;
  border-radius: 8px;
  background-color: #3182ce;
  color: white;
  text-decoration: none;
}

Разбор:

  • .header — flex-контейнер с тремя детьми: логотип, меню, кнопка;
  • space-between прижимает логотип влево, кнопку вправо, а меню ставит посередине;
  • align-items: center выравнивает всех троих по вертикали, хотя у логотипа шрифт крупнее;
  • .nav — вложенный flex-контейнер: ссылки меню стоят в ряд с промежутком 24px.

Это нормальная практика: flex-элемент сам может быть flex-контейнером для своих детей.

Псевдокласс :hover

Псевдокласс — это состояние элемента, которое пишется через двоеточие после селектора. :hover срабатывает, когда на элемент наведён курсор мыши.

.btn {
  background-color: #3182ce;
}

.btn:hover {
  background-color: #2b6cb0;   /* при наведении — темнее */
}

Пока мышь над кнопкой, действует второе правило; убрали мышь — стиль вернулся.

Плавность: transition

Без плавности стиль меняется резко. Свойство transition делает переход анимированным:

.btn {
  background-color: #3182ce;
  transition: background-color 0.2s ease;
}

.btn:hover {
  background-color: #2b6cb0;
}

transition пишут в обычном состоянии, а не в :hover: тогда анимация работает и при наведении, и когда курсор уходит. Формат: какое свойство, длительность, характер движения (ease, linear, ease-in-out). Чтобы анимировать всё сразу, пишут transition: all 0.2s, но лучше перечислять конкретные свойства.

Эффекты при наведении

.card {
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  padding: 24px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  transform: translateY(-4px);                    /* приподнять на 4px */
  box-shadow: 0 10px 20px rgb(0 0 0 / 12%);       /* добавить тень */
}

.nav a:hover {
  color: white;
  text-decoration: underline;
}
  • transform: translateY(-4px) сдвигает элемент вверх, не толкая соседей;
  • box-shadow — тень: сдвиг по X, сдвиг по Y, размытие, цвет;
  • .nav a:hover — стиль для ссылки меню под курсором.

Наведение на родителя меняет ребёнка

.card-title {
  color: #2d3748;
  transition: color 0.2s;
}

.card:hover .card-title {
  color: #3182ce;
}

«Когда курсор над карточкой, заголовок внутри неё становится синим». Так целая карточка реагирует на наведение, а не только текст.

Родственные псевдоклассы

Псевдокласс Когда срабатывает
:hover Курсор над элементом
:active В момент нажатия
:focus Элемент в фокусе (клик по полю ввода, переход клавишей Tab)
:focus-visible Фокус с клавиатуры — для видимой обводки
:first-child / :last-child Первый / последний ребёнок у родителя

Собираем: ряд карточек

<section class="cards">
    <article class="card">
        <h3 class="card-title">HTML</h3>
        <p>Структура страницы</p>
    </article>
    <article class="card">
        <h3 class="card-title">CSS</h3>
        <p>Оформление и раскладка</p>
    </article>
    <article class="card">
        <h3 class="card-title">JavaScript</h3>
        <p>Поведение и интерактивность</p>
    </article>
</section>
.cards {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  padding: 32px;
}

.card {
  flex: 1 1 250px;
  padding: 24px;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  background-color: white;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 20px rgb(0 0 0 / 12%);
}

flex: 1 1 250px означает: «начни с 250px, растягивайся, если есть место, сжимайся, если нет». На широком экране карточки делят ряд поровну, на узком — переносятся.

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

  • display: flex написан элементу, а не родителю — ничего не происходит.
  • justify-content и align-items перепутаны после смены flex-direction: column — помните, что оси поворачиваются вместе с направлением.
  • align-items: center не центрирует по вертикали — у контейнера нет высоты, центрировать не в чем.
  • transition написан только в :hover — анимация есть при наведении, но не при уходе курсора.

Подробный разбор с интерактивными примерами — MDN: Flexbox (на английском) и справочник по свойству flex.

Практика

На занятии ментор даст дополнительные задания по Flexbox и разберёт ваши решения. Для самостоятельной работы:

  1. Центр экрана. Поставьте блок 200×200px точно по центру окна браузера. Результат: блок посередине при любом размере окна.
  2. Меню. Сделайте горизонтальное меню из 5 ссылок с gap: 20px; при наведении ссылка меняет цвет плавно за 0.2s. Результат: меню в ряд, ссылки подсвечиваются.
  3. Шапка. Повторите шапку из урока: логотип слева, меню посередине, кнопка справа, всё выровнено по вертикали. У кнопки — :hover с затемнением фона. Результат: аккуратная шапка как на настоящем сайте.
  4. Карточки. Сверстайте 6 карточек с flex-wrap: wrap, при наведении карточка приподнимается и получает тень, а её заголовок меняет цвет. Результат: при сужении окна карточки переносятся, ховер работает плавно.
  5. Макет с боковой панелью. Сделайте aside фиксированной ширины 250px слева и main, занимающий всё остальное место (flex: 1). Результат: при изменении ширины окна меняется только main.

Итоги

  • display: flex пишется родителю; его прямые дети становятся flex-элементами.
  • flex-direction задаёт главную ось; justify-content выравнивает по ней, align-items — по поперечной.
  • gap — промежутки между элементами, flex-wrap: wrap — перенос на новую строку.
  • flex: 1 позволяет элементу занять всё свободное место.
  • :hover меняет стиль при наведении, transition в обычном состоянии делает это плавным.
  • Не полагайтесь только на :hover: дублируйте стили в :focus-visible.
Отзыв