CSS функции: линейные градиенты

Чтение — около 7 минут· без учёта практики

В предыдущем уроке у карточек появились тени. Теперь оформим фон подписи. Градиент — изображение, которое браузер строит из заданных цветов. Его задают в background-image или сокращённом background, как и обычный фон из урока о фоне. Картинку для такого фона скачивать не нужно.

Два цвета — один градиент

CSS
.card {
  background-color: orange; /* однотонный запасной фон */
  background-image: linear-gradient(orange, black);
}

Функция linear-gradient() принимает как минимум две цветовые остановки. Без направления первый цвет находится сверху, последний снизу; между ними браузер строит плавный переход. Градиент — значение типа изображения, поэтому background-color: linear-gradient(...) недопустимо.

Четыре двухцветных градиента: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Четыре двухцветных градиента</title>
  </head>
  <body>
    <div class="samples">
      <div class="sample one"></div>
      <div class="sample two"></div>
      <div class="sample three"></div>
      <div class="sample four"></div>
    </div>
  </body>
</html>
style.css
CSS
body {
    font-family: system-ui, sans-serif;
    margin: 1rem;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
.sample {
    width: 100px;
    height: 100px;
    display: grid;
    place-items: center;
    text-align: center;
}
.one {
    background-image: linear-gradient(orange, black);
}
.two {
    background-image: linear-gradient(green, red);
}
.three {
    background-image: linear-gradient(red, moccasin);
}
.four {
    background-image: linear-gradient(yellow, blue);
}

В примере сохранены сочетания orange/black, green/red, red/moccasin и yellow/blue. Можно использовать имена цветов и все изученные в уроке о цвете записи: HEX, RGB и HSL, в том числе с прозрачностью.

Направление ключевыми словами

Перед цветами можно указать, куда идёт переход. Это направление к конечной стороне, а не положение первого цвета.

Ключевые слова направления градиента
Направление Переход от первого цвета к последнему
to top Снизу вверх
to right Слева направо
to bottom Сверху вниз; направление по умолчанию
to left Справа налево
to top right К правому верхнему углу
to bottom right К правому нижнему углу
to bottom left К левому нижнему углу
to top left К левому верхнему углу

В паре слов порядок не важен: to top right и to right top равносильны. Сравните восемь направлений: в каждом примере первый цвет — полупрозрачный красный, второй — серый.

Восемь направлений градиента: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Восемь направлений градиента</title>
  </head>
  <body>
    <div class="samples">
      <div class="sample d0">to left</div>
      <div class="sample d1">to right</div>
      <div class="sample d2">to top</div>
      <div class="sample d3">to bottom</div>
      <div class="sample d4">to left top</div>
      <div class="sample d5">to right top</div>
      <div class="sample d6">to left bottom</div>
      <div class="sample d7">to right bottom</div>
    </div>
  </body>
</html>
style.css
CSS
body {
    font-family: system-ui, sans-serif;
    margin: 1rem;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
.sample {
    width: 100px;
    height: 100px;
    display: grid;
    place-items: center;
    text-align: center;
}
.d0 {
    background-image: linear-gradient(to left, rgb(255 0 0 / 40%), #999);
}
.d1 {
    background-image: linear-gradient(to right, rgb(255 0 0 / 40%), #999);
}
.d2 {
    background-image: linear-gradient(to top, rgb(255 0 0 / 40%), #999);
}
.d3 {
    background-image: linear-gradient(to bottom, rgb(255 0 0 / 40%), #999);
}
.d4 {
    background-image: linear-gradient(to left top, rgb(255 0 0 / 40%), #999);
}
.d5 {
    background-image: linear-gradient(to right top, rgb(255 0 0 / 40%), #999);
}
.d6 {
    background-image: linear-gradient(to left bottom, rgb(255 0 0 / 40%), #999);
}
.d7 {
    background-image: linear-gradient(to right bottom, rgb(255 0 0 / 40%), #999);
}

Направление углом

Угол отсчитывают от направления вверх по часовой стрелке: 0deg — вверх, 90deg — вправо, 180deg — вниз, 270deg — влево. Можно использовать отрицательные углы, значения больше полного оборота и другие единицы угла: -90deg, 270deg и 0.75turn задают одно направление.

Направления линейного градиента: 0 градусов вверх, 90 вправо, 180 вниз, 270 влево. Угол увеличивается по часовой стрелке, стрелки идут от первого цвета к последнему.
Угол задаёт направление перехода от начального цвета к конечному.

Линия градиента проходит через центр области фона. На квадрате 45deg равнозначно to top right, а 135deg, 225deg и 315deg соответствуют остальным углам. На прямоугольнике направление в угол зависит от пропорций: линия перпендикулярна диагонали, соединяющей два соседних с выбранным угла. Поэтому на прямоугольнике шириной 200px и высотой 100px to top right соответствует примерно 26.565deg, а не 45deg.

Углы и направление в угол прямоугольника: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Углы и направление в угол прямоугольника</title>
  </head>
  <body>
    <div class="samples">
      <figure>
        <div class="sample up"></div>
        <figcaption>0deg</figcaption>
      </figure>
      <figure>
        <div class="sample right"></div>
        <figcaption>90deg</figcaption>
      </figure>
      <figure>
        <div class="sample down"></div>
        <figcaption>180deg</figcaption>
      </figure>
      <figure>
        <div class="sample left"></div>
        <figcaption>270deg</figcaption>
      </figure>
    </div>
    <p>Квадрат: 45deg и to top right</p>
    <div class="samples">
      <div class="sample square degrees"></div>
      <div class="sample square corner"></div>
    </div>
    <p>Прямоугольник: 45deg, to top right, 26.565deg</p>
    <div class="samples">
      <div class="sample rectangle degrees"></div>
      <div class="sample rectangle corner"></div>
      <div class="sample rectangle equivalent"></div>
    </div>
    <p>Три цвета: blue, white, red</p>
    <div class="tricolor"></div>
  </body>
</html>
style.css
CSS
body {
    font-family: system-ui, sans-serif;
    margin: 1rem;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
.sample {
    width: 100px;
    height: 100px;
    display: grid;
    place-items: center;
    text-align: center;
}
.up {
    background-image: linear-gradient(0deg, red, blue);
}
.right {
    background-image: linear-gradient(90deg, red, blue);
}
.down {
    background-image: linear-gradient(180deg, red, blue);
}
.left {
    background-image: linear-gradient(270deg, red, blue);
}
.degrees {
    background-image: linear-gradient(45deg, red, blue);
}
.corner {
    background-image: linear-gradient(to top right, red, blue);
}
.rectangle {
    width: 200px;
    max-width: 100%;
    height: 100px;
}
.equivalent {
    background-image: linear-gradient(26.565051deg, red, blue);
}
.tricolor {
    height: 100px;
    background-color: blue;
    background-image: linear-gradient(135deg, blue, white, red);
}

.samples figure {
    margin: 0;
}

У прямоугольника с отношением ширины к высоте 2 к 1 линия to top right перпендикулярна диагонали от верхнего левого до нижнего правого угла и соответствует примерно 26.6 градуса. Направление 45deg отличается.
Для прямоугольника 2:1 направление to top right отличается от 45deg.

Цветовые остановки

CSS
background-image: linear-gradient(to right, red 15%, green 60%, blue 70%);

Остановка задаёт цвет и положение на линии градиента, например в процентах, px, em или rem. Проценты относятся к длине линии градиента, а не всегда к ширине блока: при вертикальном направлении это его высота. За первой и последней остановками обычный линейный градиент продолжает крайние цвета.

Если крайние позиции пропущены, они становятся 0% и 100%. Неуказанные промежуточные позиции распределяются равномерно между ближайшими заданными. Например, в red 80%, green, blue зелёная остановка окажется на 90%, а синяя — на 100%: красный занимает начало до 80%, затем идут два коротких перехода. Это не три одинаковые полосы.

Пять вариантов цветовых остановок: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Пять вариантов цветовых остановок</title>
  </head>
  <body>
    <p>red 5%, green, blue</p>
    <div class="stops s5"></div>
    <p>red 10%, green, blue</p>
    <div class="stops s10"></div>
    <p>red 50%, green, blue</p>
    <div class="stops s50"></div>
    <p>red 80%, green, blue</p>
    <div class="stops s80"></div>
    <p>red 15%, green 60%, blue 70%</p>
    <div class="stops explicit"></div>
  </body>
</html>
style.css
CSS
body {
    font-family: system-ui, sans-serif;
    margin: 1rem;
}
.stops {
    height: 80px;
    margin-block: 1rem;
    color: white;
}
.s5 {
    background-image: linear-gradient(to right, red 5%, green, blue);
}
.s10 {
    background-image: linear-gradient(to right, red 10%, green, blue);
}
.s50 {
    background-image: linear-gradient(to right, red 50%, green, blue);
}
.s80 {
    background-image: linear-gradient(to right, red 80%, green, blue);
}
.explicit {
    background-image: linear-gradient(to right, red 15%, green 60%, blue 70%);
}

Для ясного результата записывайте позиции по возрастанию. Если следующая меньше предыдущей, браузер поднимет её до предыдущей позиции, а не развернёт переход назад. Допустимы и позиции за пределами 0–100%: видимая область покажет только часть перехода.

Резкие границы и прозрачность

Когда две соседние остановки разных цветов стоят в одном месте, плавного участка между ними нет. Так получаются цветные полосы:

CSS
background-image: linear-gradient(to right, red 0 50%, blue 50% 100%);
/* red 0 50% — сокращение для red 0, red 50% */

Прозрачность — часть цвета. Через прозрачные участки виден следующий слой фона или background-color. Для затемнения фотографии можно использовать один тёмный цвет с разной прозрачностью: rgb(57 35 19 / 95%) и rgb(57 35 19 / 0%). Сам градиент не гарантирует читаемость — область прямо под текстом должна оставаться достаточно тёмной.

Несколько фоновых градиентов

Фоновые изображения перечисляют через запятую. Первый слой рисуется сверху, следующие под ним. Чтобы нижние слои были видны, верхний должен содержать прозрачные участки. Цвет фона находится под всеми изображениями. В сокращённом background также задаются размер и повторение фона; не перезаписывайте их случайно, если меняете только градиент.

Узор из трёх градиентов: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Узор из трёх градиентов</title>
  </head>
  <body>
    <div class="pattern"></div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
}
.pattern {
    width: 240px;
    max-width: 100%;
    height: 160px;
    background-image:
        linear-gradient(red 30%, transparent 30%, transparent 70%, red 70%),
        linear-gradient(
            90deg,
            red 31%,
            transparent 31%,
            transparent 42%,
            white 42%,
            white 58%,
            transparent 58%,
            transparent 69%,
            red 69%
        ),
        linear-gradient(red 42%, white 42%, white 58%, red 58%);
}

В этом примере сохранён узор из трёх красно-белых градиентов: прозрачные промежутки первых двух открывают третий. Измените transparent на непрозрачный цвет и посмотрите, какие части узора пропадут.

Повторяющиеся линейные градиенты

Функция repeating-linear-gradient() повторяет участок между первой и последней остановками в обе стороны. Длина периода — разность их позиций: от 20px до 50px — период 30px, а от 5% до 15% — 10% длины линии. Это повторение цветов внутри изображения, а не background-repeat, которое повторяет изображение целиком.

Плавные и резкие повторяющиеся полосы: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Плавные и резкие повторяющиеся полосы</title>
  </head>
  <body>
    <div class="band smooth-px"></div>
    <div class="band smooth-percent"></div>
    <div class="band sharp-negative"></div>
    <div class="band sharp-positive"></div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
}
.band {
    height: 100px;
    margin-block: 1rem;
}
.smooth-px {
    background-image: repeating-linear-gradient(-45deg, #777 20px, #888 40px, hsl(300 50% 50% / 90%) 50px);
}
.smooth-percent {
    background-image: repeating-linear-gradient(to top right, rgb(255 165 0 / 80%) 5%, khaki 10%, plum 15%);
}
.sharp-negative {
    background-image: repeating-linear-gradient(-45deg, red 0 10px, green 10px 20px);
}
.sharp-positive {
    background-image: repeating-linear-gradient(45deg, yellow 0 10px, black 10px 20px);
}

Первые два примера показывают плавные полосы с цветами из исходного урока. Следующие — резкие полосы по 10px, под углами -45deg и 45deg. Запись red 0 10px, green 10px 20px равнозначна четырём остановкам red 0, red 10px, green 10px, green 20px. Если начало и конец имеют разный цвет, на стыке повторов будет резкая граница; для плавного стыка их цвета должны совпадать.

Несколько повторяющихся градиентов

Повторяющиеся градиенты тоже можно наложить друг на друга. В сохранённом примере первый слой содержит красный с прозрачностью 30% и зелёный, второй — жёлтый и чёрный. Полупрозрачный красный открывает цвета нижнего слоя, а непрозрачный зелёный закрывает их.

Пересекающиеся повторяющиеся градиенты: попробуйте сами

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Пересекающиеся повторяющиеся градиенты</title>
  </head>
  <body>
    <div class="pattern"></div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
}
.pattern {
    height: 135px;
    background-image:
        repeating-linear-gradient(-45deg, rgb(255 0 0 / 30%) 0 10px, green 10px 20px),
        repeating-linear-gradient(45deg, yellow 0 10px, black 10px 20px);
}

Градиент под подписью

Меняйте угол, непрозрачность и положение конца тёмного участка. Следите, чтобы текст не попадал в прозрачную часть. Полный генератор позволяет добавить другие цвета, остановки и слои.

Тони с любимым канатом

Моя любимая игрушка

Включите JavaScript для настройки. Подпись с градиентом и готовый CSS доступны без него.

Фон подписи

background-image: linear-gradient(0deg, rgb(57 35 19 / 0.95) 0%, rgb(57 35 19 / 0.95) 70%, rgb(57 35 19 / 0) 100%);

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

Проверьте себя

Выберите ответ — объяснение появится сразу. Ответ можно менять.

1. В каком свойстве допустимо linear-gradient(red, blue)?
Показать ответ

background-image — Градиент — изображение, а не цвет.

2. Куда идёт градиент без указанного направления?
Показать ответ

Сверху вниз: to bottom — Первый цвет сверху, последний снизу.

3. Какому направлению соответствует 90deg?
Показать ответ

Слева направо — Ноль — вверх, положительные углы отсчитываются по часовой стрелке.

4. Всегда ли 45deg равно to top right?
Показать ответ

Нет: совпадают на квадрате, а у прямоугольника направление в угол зависит от пропорций — На прямоугольнике 2:1 направление to top right соответствует примерно 26.565deg.

5. Где окажется green в linear-gradient(to right, red 80%, green, blue)?
Показать ответ

На 90% — Промежуточная остановка распределяется между 80% и последней 100%.

6. Что создаёт резкую границу двух цветов?
Показать ответ

Две соседние остановки разных цветов в одной позиции — Между ними нет расстояния для плавного перехода.

7. Почему нижний градиент не виден под верхним?
Показать ответ

Верхний слой может быть полностью непрозрачным — Первый фон рисуется сверху. Для нижних слоёв нужны прозрачные участки верхних.

8. Каков период repeating-linear-gradient(red 20px, blue 50px)?
Показать ответ

30px — Период равен разности последней и первой позиции: 50 − 20.

Задание

Ожидаемый результат: все шесть подписей находятся внизу фотографий. Тёмная подложка исчезает вверх, прямо за текстом остаётся почти непрозрачной. Контур любимой карточки и её внешняя тень сохранены. На узком экране подписи переносятся и остаются читаемыми.

Выполните задание

css/style.css
CSS
/* Общие стили сайта Тони */

/* Ширина и высота включают внутренние отступы и рамку — у всех элементов и псевдоэлементов */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Палитра сайта в пользовательских свойствах: цвет меняется в одном месте */
:root {
  --accent: saddlebrown; /* заголовки, меню, акценты */
  --text: #2b211b; /* основной текст */
  --text-muted: #6b5d52; /* подписи и второстепенный текст */
}

/* Шрифт заголовков Nunito (лицензия SIL Open Font License): кириллица и латиница в разных файлах */
@font-face {
  font-family: "Nunito";
  src: url("../assets/nunito-cyrillic-800-normal.woff2") format("woff2");
  font-weight: 800;
  font-display: swap; /* пока шрифт загружается, текст виден запасным шрифтом */
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: "Nunito";
  src: url("../assets/nunito-latin-800-normal.woff2") format("woff2");
  font-weight: 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2000-206F, U+20AC, U+2122;
}

body {
  background-color: linen;
  color: var(--text); /* цвет для потомков без собственного значения */
  font-size: 1.0625rem; /* 17px при настройке браузера по умолчанию */
  font-family:
    system-ui,
    -apple-system,
    "Segoe UI",
    Roboto,
    sans-serif; /* системный шрифт устройства */
  line-height: 1.6; /* множитель: у каждого элемента интервал от его собственного размера шрифта */
  margin: 0; /* убираем отступ браузера: шапка и подвал во всю ширину окна */
}

/* Поля основного содержимого: текст не прижимается к краям окна */
main {
  padding: 1.5rem 1rem;
  max-width: 68rem; /* на широком экране строки не растягиваются на всё окно */
  margin: 0 auto; /* колонка по центру */
}

/* Изображения и видео не шире колонки, пропорции сохраняются */
img,
video {
  max-width: 100%;
  height: auto;
}

/* Фреймы, аудиоплеер, индикаторы и поля формы не выходят за колонку */
progress,
meter,
iframe,
audio,
input,
select,
textarea,
button {
  max-width: 100%;
}

/* Разрешаем группе полей сужаться вместе с колонкой. */
fieldset {
  min-width: 0;
}

/* Общий цвет заголовков — групповой селектор */
h1,
h2 {
  color: var(--accent);
  font-family: "Nunito", "Trebuchet MS", system-ui, sans-serif;
  font-weight: 800;
  line-height: 1.2; /* крупному тексту хватает меньшего интервала */
}

/* Размеры заголовков в rem — от размера шрифта корня, а не родителя */
h1 {
  text-align: center;
  font-size: 2.5rem;
}

h2 {
  font-size: 1.75rem;
}

/* Вводный блок главной страницы */
.intro {
  background-color: hsl(30, 80%, 95%);
  padding: 1.5rem; /* фон вокруг текста; заодно не выпускает margin заголовка за пределы блока */
  border-radius: 16px; /* скругляется и фон, хотя рамки нет */
  /* Лапка в правом нижнем углу; цвет фона выше остаётся запасным, если картинка не загрузится */
  background-image: url("../assets/paw.svg");
  background-repeat: no-repeat;
  background-position: right 1rem bottom 1rem;
  background-size: 4rem;
}

/* Разделы главной — светлые карточки на фоне страницы */
main > section {
  background-color: #fffaf3;
  border: 1px solid hsl(28, 30%, 82%);
  border-radius: 16px;
  padding: 1.5rem;
  margin-top: 1.5rem;
}

/* Ширина вводных абзацев ограничена 60ch; количество букв в строке зависит от шрифта и текста */
.intro p {
  max-width: 60ch;
}

/* Важное предупреждение во вводном блоке — темнее основного акцента */
.intro strong {
  color: #6e3510;
}

/* Оглавление главной страницы — переносимый ряд «таблеток» на Flexbox */
.toc {
  background-color: white;
  border: 1px solid tan;
  display: flex;
  flex-wrap: wrap; /* «таблетки», которым не хватает места, переходят на новую строку */
  gap: 0.5rem; /* промежутки между «таблетками» и между строками */
  list-style: none;
  padding: 0.75rem;
}

/* Подчёркивание ссылок чуть ниже букв: не задевает выносные элементы */
a {
  text-underline-offset: 3px;
}

/* Тонкая линия отделяет шапку от страницы */
header {
  border-bottom: 1px solid tan;
}

/* Меню на Flexbox: пункты могут переноситься, промежутки задаёт gap */
nav ul {
  margin: 0;
  padding: 0.5rem 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
}

/* Ссылка на запись стоит отдельно у правого края: автоматический отступ забирает свободное место */
nav li:last-child {
  margin-left: auto;
}

/* Ссылки меню в шапке — селектор потомков */
nav a {
  color: var(--accent);
  text-decoration: none;
  display: inline-block; /* у строчной ссылки вертикальный padding не раздвигает строку */
  padding: 0.4rem 0.9rem;
  border-radius: 999px; /* «таблетка»: радиус больше половины высоты */
}

/* Ссылки оглавления главной страницы */
.toc a {
  color: var(--text-muted); /* тёплый серый: контраст 6.34:1 на белом фоне оглавления */
  display: block; /* ссылка заполняет «таблетку» целиком */
  padding: 0.25rem 0.75rem;
  border: 1px solid tan;
  border-radius: 999px;
  text-decoration: none;
}

/* Фотографии со скруглёнными углами */
figure img {
  border-radius: 8px;
}

figcaption {
  color: var(--text-muted); /* тёплый серый: контраст 5.64:1 на фоне linen */
  font-size: 0.95rem;
}

footer {
  background-color: hsl(30, 50%, 90%);
  text-align: center;
  padding: 1rem;
}

/* Ссылка в подвале берёт цвет текста подвала */
footer a {
  color: inherit;
}

/* Поля формы получают шрифт страницы вместо шрифта браузера */
input,
select,
textarea,
button {
  font: inherit;
}

/* Ссылки темнеют при наведении указателя */
a:hover {
  color: #5c2d0c;
}

/* Ссылки меню подчёркиваются при наведении и фокусе с клавиатуры */
nav a:hover,
nav a:focus-visible {
  text-decoration: underline;
}

/* Заметная рамка фокуса у ссылок, полей и кнопок при работе с клавиатуры */
:focus-visible {
  outline: 3px solid #1f6feb;
  outline-offset: 3px;
}

/* Цитата из книги — шрифтом с засечками и курсивом */
blockquote {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  border-left: 4px solid tan; /* черта слева выделяет цитату */
  padding-left: 1rem;
}

/* Подпись под цитатой — у правого края */
blockquote + figcaption {
  text-align: right;
}

/* Строки формы на Flexbox: подпись — колонка постоянной ширины, поле занимает остаток строки,
   а на узком экране переносится под подпись */
fieldset p {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}

fieldset p label {
  flex: 0 1 12rem; /* не растёт, но может сжаться в узкой строке */
  min-width: 0;
}

fieldset p input,
fieldset p select,
fieldset p textarea {
  flex: 1 1 12rem; /* растёт и сжимается от исходной ширины 12rem */
  min-width: 0; /* поле может стать уже своего автоматического минимума */
}

/* Фотоальбом на Grid. Все правила альбома собраны во вложенный блок: & — сам .gallery */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1rem;
  list-style: none;
  padding: 0;

  /* Карточка — точка отсчёта для значка */
  & li {
    position: relative;
  }

  /* Карточка со значком выделена рамкой */
  & li:has(.badge) {
    outline: 3px solid tan;
    outline-offset: 4px;
    border-radius: 8px;
  }

  & figure {
    margin: 0;
  }

  & img {
    width: 100%; /* фото заполняет ширину своей ячейки */
  }
}

/* Список команд: первая колонка подстраивается под текст, оставляя место для перевода. */
dl {
  display: grid;
  grid-template-columns: fit-content(calc(50% - 0.5rem)) minmax(0, 1fr);
  gap: 0.5rem 1rem;
}

dt {
  overflow-wrap: anywhere;
  grid-column: 1;
  font-weight: bold;
}

dd {
  grid-column: 2;
  overflow-wrap: anywhere; /* длинный перевод переносится внутри своей колонки */
  margin: 0; /* браузерный отступ слева больше не нужен: колонку задаёт сетка */
}

/* Длинные слова и адреса переносятся, а не выходят за край узкого экрана */
body {
  overflow-wrap: break-word;
}

/* Таблица режима дня на узком экране прокручивается в своей рамке, а не сжимается до нечитаемых колонок */
.table-wrap {
  overflow-x: auto;
}

table {
  width: 100%;
  min-width: 28rem;
}

/* Подпись таблицы прокручивается вместе с ней: прижимаем её к началу, чтобы она была видна сразу */
caption {
  text-align: left;
}

/* Широкий экран: в разделе «Моя любимая игрушка» фото справа, текст слева.
   На узком экране правило не действует, и раздел остаётся одной колонкой. */
@media (width >= 48rem) {
  .toy {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    column-gap: 1.5rem;
  }

  .toy figure {
    grid-column: 2;
    grid-row: 1 / span 6; /* фото рядом со всеми абзацами раздела */
    margin: 0;
  }

  .toy hr {
    width: 100%;
  }
}

/* Значок «Любимая» в правом верхнем углу карточки; из потока исключён, поэтому фото не сдвигается */
.badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  background-color: var(--accent);
  color: white;
  font-size: 0.85rem;
  font-weight: bold;
}

/* Шапка прилипает к верхнему краю окна. Фон нужен, чтобы под шапкой не просвечивал текст страницы */
header {
  position: sticky;
  top: 0;
  z-index: 10; /* выше карточек альбома: они тоже позиционированы и записаны в HTML позже шапки */
  background-color: linen;
}

/* Липкая шапка должна быть низкой: пункты меню не сжимаются и не переносятся на вторую строку,
   а если им не хватает места, меню прокручивается внутри шапки */
nav ul {
  flex-wrap: nowrap;
  overflow-x: auto;
}

nav li {
  flex-shrink: 0;
}

/* Заголовок, к которому ведёт ссылка-якорь, не прячется под липкой шапкой.
   В rem: при увеличении текста растут и шапка, и отступ */
[id] {
  scroll-margin-top: 4.5rem;
}

/* Ссылка, которая открывается в новой вкладке, помечена стрелкой — посетитель знает, что страница не сменится */
a[target="_blank"]::after {
  content: " ↗";
}

/* Ссылка на электронную почту — с конвертом */
a[href^="mailto:"]::before {
  content: "✉ ";
}

/* Первая ссылка меню — «Главная» — подписана лапкой без изменения HTML */
nav li:first-child a::before {
  content: "🐾 ";
}

/* Первый абзац вводного блока крупнее остальных. :first-child здесь не подойдёт — первым стоит заголовок h1 */
.intro p:first-of-type {
  font-size: 1.2rem;
}

/* Список игрушек: вместо стандартного кружка — косточка */
.toy-list li::marker {
  content: "🦴 ";
}

/* Номера шагов в нумерованных списках — в цвет заголовков и жирные; текст пунктов не меняется */
ol li::marker {
  color: var(--accent);
  font-weight: bold;
}

/* Таблица режима дня: соседние рамки объединяются в одну линию */
table {
  border-collapse: collapse;
}

/* Ячейки с воздухом и линией снизу; текст — от левого края и сверху, даже если в соседней ячейке две строки */
th,
td {
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid tan;
  text-align: left;
  vertical-align: top;
}

/* Шапка таблицы — в цвет заголовков сайта */
thead th {
  background-color: var(--accent);
  color: white;
}

/* Чётные строки тела таблицы — на светлом фоне: глаз не теряет строку */
tbody tr:nth-child(even) {
  background-color: hsl(30, 80%, 95%);
}

/* Отдельные рамки без промежутков позволяют скруглить крайние ячейки. Линия только снизу не удваивается. */
table {
  border-collapse: separate;
  border-spacing: 0;
}
thead th:first-child {
  border-top-left-radius: 8px;
}

thead th:last-child {
  border-top-right-radius: 8px;
}

/* Форма записи: текстовые поля, список и область текста в едином стиле */
input:not([type="radio"], [type="checkbox"]),
select,
textarea {
  padding: 0.5rem 0.75rem;
  border: 1px solid #8a7660;
  border-radius: 8px;
  background-color: white;
}

/* Поле «Ведёт прогулку» менять нельзя — фон показывает это */
input[readonly] {
  background-color: hsl(30, 80%, 95%);
}

/* Отмеченные флажки и переключатели — в цвет сайта */
input[type="radio"],
input[type="checkbox"] {
  accent-color: var(--accent);
}

/* Неверно заполненное поле — после того как посетитель с ним поработал */
input:user-invalid,
select:user-invalid,
textarea:user-invalid {
  border-color: firebrick;
}

/* Группы полей — светлые блоки с тонкой рамкой вместо рельефной рамки браузера */
fieldset {
  margin-bottom: 1rem;
  border: 1px solid #8a7660;
  border-radius: 16px;
}

/* Группа полей, в которой сейчас работает посетитель */
fieldset:focus-within {
  border-color: var(--accent);
}

/* Кнопки-«таблетки»: основная залита цветом, второстепенная — только рамка */
button {
  padding: 0.7rem 1.4rem;
  border: 2px solid var(--accent);
  border-radius: 999px;
  background-color: var(--accent);
  color: white;
  font-weight: bold;
  cursor: pointer;
}

button[type="reset"] {
  background-color: transparent;
  color: var(--accent);
}

/* Текст ошибок связан с полями через aria-describedby и не полагается только на цвет рамки. */
.field-error {
  display: none;
  flex-basis: 100%;
  color: firebrick;
  font-weight: bold;
}
input:user-invalid + .field-error {
  display: block;
}

/* Фото любимой игрушки — как карточка-полароид: слегка повёрнуто вокруг нижнего края, будто прижато к столу */
.toy img {
  transform: rotate(-2deg);
  transform-origin: 50% 100%;
}

/* Под указателем фото выпрямляется и чуть увеличивается */
.toy img:hover {
  transform: rotate(0deg) scale(1.03);
}

/* «Таблетка» оглавления под указателем и в фокусе приподнимается: отдельное свойство translate */
.toc a:hover,
.toc a:focus-visible {
  translate: 0 -2px;
}

/* Нажатая кнопка будто вдавливается */
button:active {
  transform: translateY(1px) scale(0.98);
}

/* Плавные изменения: переходим конкретные свойства, а не all */
.toy img {
  transition: transform 0.4s ease-out; /* фото выпрямляется плавно */
}

.toc a {
  transition: translate 0.2s ease-out;
}

button {
  transition:
    background-color 0.2s,
    transform 0.1s;
}

/* Посетитель попросил систему уменьшить движение — изменения происходят сразу, без переходов */
@media (prefers-reduced-motion: reduce) {
  .toy img,
  .toc a,
  button {
    transition: none;
  }
}

/* Лапка у «Главной» машет, когда на ссылку наводят указатель */
@keyframes wag {
  0%,
  100% {
    rotate: 0deg;
  }
  25% {
    rotate: -18deg;
  }
  75% {
    rotate: 18deg;
  }
}

nav li:first-child a::before {
  display: inline-block; /* строчный псевдоэлемент не поворачивается */
  transform-origin: 50% 80%; /* лапка качается от «запястья» */
}

nav li:first-child a:hover::before,
nav li:first-child a:focus-visible::before {
  animation-name: wag;
  animation-duration: 0.5s;
  animation-timing-function: ease-in-out;
  animation-iteration-count: 2; /* помахать дважды и остановиться */
}

/* Приветствие на главной плавно появляется снизу при загрузке страницы */
@keyframes rise {
  from {
    opacity: 0;
    translate: 0 16px;
  }
  /* to не задан: конечный кадр — обычный вид элемента */
}

.intro > * {
  /* both: до старта — первый кадр (элемент скрыт во время задержки), после — последний */
  animation: rise 0.6s ease-out both;
}

/* Строки появляются друг за другом */
.intro > :nth-child(2) {
  animation-delay: 0.1s;
}

.intro > :nth-child(3) {
  animation-delay: 0.2s;
}

.intro > :nth-child(4) {
  animation-delay: 0.3s;
}

/* Посетитель попросил уменьшить движение — анимаций нет, всё видно сразу */
@media (prefers-reduced-motion: reduce) {
  .intro > *,
  nav li:first-child a:hover::before,
  nav li:first-child a:focus-visible::before {
    animation: none;
  }
}

button:hover,
button:focus-visible {
  background-color: #6b3513;
}

button[type="reset"]:hover,
button[type="reset"]:focus-visible {
  background-color: hsl(30, 70%, 90%);
}

/* Подписи читаются на тёмной подложке и без декоративной тени. */
.gallery figcaption {
  padding: 0.5rem;
  color: white;
  background-color: hsl(25, 50%, 15%);
  text-shadow: 0 1px 3px rgb(0 0 0 / 60%);
}

/* Два слоя: чёткая тень у края и широкая мягкая тень под карточкой. */
.gallery li {
  background-color: #fffaf3;
  border-radius: 12px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 8%), 0 8px 24px rgb(0 0 0 / 8%);
}

/* Внутренняя тень дополняет уже знакомое преобразование нажатой кнопки. */
button:active {
  box-shadow: inset 0 2px 3px rgb(0 0 0 / 25%);
}
index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Сайт карликового пинчера Тони: игрушки, режим дня и запись на прогулку.">
    <title>Сайт Тони</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <header>
      <!-- Меню сайта -->
      <nav>
        <ul>
          <li><a href="index.html">Главная</a></li>
          <li><a href="pages/toys.html">Мои игрушки</a></li>
          <li><a href="pages/signup.html">Запись на прогулку</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <div class="intro">
        <h1 id="top">Привет! Меня зовут Тони</h1>
        <p>Я карликовый пинчер. Мне три года, я вешу 8&nbsp;кг и люблю гулять в любую погоду. Хозяин говорит: <q>Тони — лучший сторож в доме</q>.</p>
        <p>Мой день рождения — <time datetime="2023-09-22">22 сентября</time>. <strong>Пожалуйста, не кормите меня со стола!</strong></p>
        <p>Моя родословная записана в Российской кинологической федерации (<abbr title="Российская кинологическая федерация">РКФ</abbr>).</p>
      </div>
      <ul class="toc">
        <li><a href="#toy">Моя любимая игрушка</a></li>
        <li><a href="#commands">Команды, которые я знаю</a></li>
        <li><a href="#schedule">Мой режим дня</a></li>
        <li><a href="#walks">Где я гуляю</a></li>
        <li><a href="#song">Песня для прогулок</a></li>
      </ul>
      <section class="toy">
        <h2 id="toy">Моя любимая игрушка</h2>
        <p data-toy-id="17">Это канат с мячиком. Хозяин бросает его, а я приношу обратно — <em>почти</em> всегда. <a href="pages/toys.html">Посмотреть все игрушки Тони</a>.</p>
        <figure>
          <img src="assets/tony-toy.png" alt="Тони сидит и держит в зубах канат с красным мячиком" width="300" height="400" title="Мой канат &quot;Удав&quot;">
          <figcaption>Я и мой канат «Удав»</figcaption>
        </figure>
        <p><a href="assets/tony-toy.png" download>Скачать фотографию</a></p>
        <p>Подробнее о моей породе — в статье <a href="https://ru.wikipedia.org/wiki/Карликовый_пинчер" target="_blank">«Карликовый пинчер» в Википедии</a>.</p>
        <hr>
        <p>Вечером игра закончилась. Канат остался на полу, а я свернулся калачиком в кресле.</p>
      </section>
      <section>
        <h2>Мой стишок</h2>
        <p>
          Я маленький, но смелый,<br>
          Гуляю целый день,<br>
          А вечером на кресле<br>
          Сплю — и мне не лень.
        </p>
      </section>
      <section>
        <h2 id="commands">Команды, которые я знаю</h2>
        <p>Хозяин научил меня командам на немецком:</p>
        <dl>
          <dt lang="de">Sitz!</dt>
          <dd>сидеть</dd>
          <dt lang="de">Platz!</dt>
          <dd>лежать</dd>
          <dt lang="de">Fuß!</dt>
          <dd>рядом</dd>
        </dl>
        <details>
          <summary>Почему команды на немецком?</summary>
          <p>Карликовый пинчер — немецкая порода, и хозяину понравилась эта традиция.</p>
        </details>
      </section>
      <section>
        <h2 id="schedule">Мой режим дня</h2>
        <div class="table-wrap" tabindex="0" role="region" aria-label="Режим дня Тони">
          <table>
            <caption>Режим дня Тони</caption>
            <thead>
              <tr>
                <th scope="col">Время</th>
                <th scope="col">Будни</th>
                <th scope="col">Выходные</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <th scope="row">07:00</th>
                <td>Прогулка, 20 минут</td>
                <td>Сплю</td>
              </tr>
              <tr>
                <th scope="row">08:00</th>
                <td colspan="2">Завтрак</td>
              </tr>
              <tr>
                <th scope="row">13:00</th>
                <td>Жду хозяина и сплю</td>
                <td>Прогулка в парке, 1 час</td>
              </tr>
              <tr>
                <th scope="row">19:00</th>
                <td colspan="2">Ужин</td>
              </tr>
              <tr>
                <th scope="row">21:00</th>
                <td colspan="2">Вечерняя прогулка</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>
      <section>
        <h2 id="progress">Мои успехи</h2>
        <p>Учу команду «Апорт»: пройдено 3 занятия из 5. <progress value="3" max="5">3 из 5</progress></p>
        <p>Прогулка сегодня: 40 минут. <meter value="40" min="0" max="90" low="30" high="60" optimum="75">40 из 90 минут</meter></p>
      </section>
      <section>
        <h2 id="walks">Где я гуляю</h2>
        <p>Я люблю гулять в сквере у дома.</p>
        <p>
          <a href="pages/walks.html" target="walk">Маршрут</a> |
          <a href="assets/tony.png" target="walk">Фото с прогулки</a>
        </p>
        <iframe src="pages/walks.html" name="walk" title="Маршрут и фото прогулки Тони" width="320" height="420"></iframe>
        <h3>Видео с прогулки</h3>
        <p>Субтитры помогут, если вы смотрите видео без звука.</p>
        <video width="180" height="320" poster="assets/walk.jpg" controls>
          <source src="assets/walk.mp4" type="video/mp4">
          <track src="assets/ru.vtt" kind="subtitles" srclang="ru" label="Русский" default>
          <p><a href="assets/walk.mp4">Скачать видео</a></p>
        </video>
        <h3>Как я лаю</h3>
        <p>Так я разговариваю с хозяином — у нас целый диалог!</p>
        <audio controls>
          <source src="assets/bark.mp3" type="audio/mpeg">
          <p><a href="assets/bark.mp3">Скачать запись</a></p>
        </audio>
      </section>
      <section>
        <h2 id="song">Песня для прогулок</h2>
        <p>Хозяин включает эту песню, когда мы выходим гулять.</p>
        <iframe width="480" height="270" src="https://www.youtube.com/embed/ojULkWEUsPs?start=17" title="Baha Men — Who Let the Dogs Out" loading="lazy" allow="fullscreen"></iframe>
        <p><a href="https://www.youtube.com/watch?v=ojULkWEUsPs">Baha Men — Who Let the Dogs Out на YouTube</a></p>
      </section>
      <section>
        <h2>Моя любимая книга</h2>
        <p>Хозяин читал мне вслух рассказ <cite>«Каштанка»</cite> Антона Чехова. Он начинается так:</p>
        <figure>
          <blockquote>
            <p>Молодая рыжая собака — помесь таксы с дворняжкой — очень похожая мордой на лисицу, бегала взад и вперёд по тротуару и беспокойно оглядывалась по сторонам.</p>
          </blockquote>
          <figcaption>— Антон Чехов, <cite>«Каштанка»</cite></figcaption>
        </figure>
      </section>
      <p><a href="#top">Наверх</a></p>
    </main>
    <footer>
      <p><small>&copy; 2026 Тони и его хозяин</small></p>
      <address>Написать хозяину Тони: <a href="mailto:tony@example.com">tony@example.com</a></address>
    </footer>
  </body>
</html>
pages/signup.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Запишитесь на совместную прогулку с карликовым пинчером Тони.">
    <title>Запись на прогулку с Тони</title>
    <link rel="stylesheet" href="../css/style.css">
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li><a href="../index.html">Главная</a></li>
          <li><a href="toys.html">Мои игрушки</a></li>
          <li><a href="signup.html">Запись на прогулку</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Запись на прогулку</h1>
      <p>Гуляйте вместе с Тони! Оставьте заявку, и хозяин Тони напишет вам, чтобы договориться о встрече.</p>
      <p><a href="../index.html#walks">Где мы гуляем</a></p>
      <form>
        <fieldset>
          <legend>О вас</legend>
          <p>
            <label for="owner">Ваше имя</label>
            <input type="text" id="owner" name="owner" autocomplete="name" required aria-describedby="owner-error">
            <span class="field-error" id="owner-error">Введите ваше имя.</span>
          </p>
          <p>
            <label for="email">Электронная почта</label>
            <input type="email" id="email" name="email" autocomplete="email" placeholder="anna@example.com" required aria-describedby="email-error">
            <span class="field-error" id="email-error">Введите адрес электронной почты, например anna@example.com.</span>
          </p>
        </fieldset>
        <fieldset>
          <legend>Ваша собака</legend>
          <p>
            <label for="breed">Порода</label>
            <input type="text" id="breed" name="breed" list="breeds">
            <datalist id="breeds">
              <option value="Карликовый пинчер"></option>
              <option value="Такса"></option>
              <option value="Йоркширский терьер"></option>
              <option value="Беспородная"></option>
            </datalist>
          </p>
          <p>
            <label for="dogs">Сколько собак придёт</label>
            <input type="number" id="dogs" name="dogs" min="1" max="3" value="1" aria-describedby="dogs-error">
            <span class="field-error" id="dogs-error">Укажите число собак от 1 до 3.</span>
          </p>
        </fieldset>
        <fieldset>
          <legend>Когда гулять</legend>
          <p>
            <label for="date">Дата</label>
            <input type="date" id="date" name="date" required aria-describedby="date-error">
            <span class="field-error" id="date-error">Выберите дату прогулки.</span>
          </p>
          <label><input type="radio" name="time" value="morning" checked> Утром</label>
          <label><input type="radio" name="time" value="evening"> Вечером</label>
        </fieldset>
        <fieldset>
          <legend>Прогулка</legend>
          <p>
            <label for="leader">Ведёт прогулку</label>
            <input type="text" id="leader" name="leader" value="Тони" readonly>
          </p>
          <p>
            <label for="route">Маршрут</label>
            <select id="route" name="route">
              <optgroup label="В городе">
                <option value="square">Сквер у дома</option>
                <option value="pond" selected>Пруд с утками</option>
              </optgroup>
              <optgroup label="За городом">
                <option value="forest">Лес</option>
                <option value="river" disabled>Берег реки — закрыт до весны</option>
              </optgroup>
            </select>
          </p>
          <p>
            <label for="wishes">Пожелания</label>
            <textarea id="wishes" name="wishes" rows="4" cols="40" maxlength="300" placeholder="Например: моя собака боится велосипедов"></textarea>
          </p>
        </fieldset>
        <fieldset>
          <legend>Что взять с собой</legend>
          <label><input type="checkbox" name="take" value="ball"> Мячик</label>
          <label><input type="checkbox" name="take" value="rope"> Канат</label>
        </fieldset>
        <p>
          <button type="submit">Записаться на прогулку</button>
          <button type="reset">Очистить форму</button>
        </p>
      </form>
    </main>
    <footer>
      <p><small>&copy; 2026 Тони и его хозяин</small></p>
      <address>Написать хозяину Тони: <a href="mailto:tony@example.com">tony@example.com</a></address>
    </footer>
  </body>
</html>
pages/toys.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Любимые игрушки Тони и правила игры в канат.">
    <title>Игрушки Тони</title>
    <link rel="stylesheet" href="../css/style.css">
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li><a href="../index.html">Главная</a></li>
          <li><a href="toys.html">Мои игрушки</a></li>
          <li><a href="signup.html">Запись на прогулку</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <h1>Мои игрушки</h1>
      <ul class="toy-list">
        <li>Канат с мячиком «Удав» — моя любимая игрушка</li>
        <li>Резиновая косточка — для вечеров, когда гулять уже поздно</li>
        <li>Пищащий ёжик — его я прячу <del>под диваном</del> <ins>в своей лежанке</ins></li>
      </ul>
      <img src="../assets/tony.png" alt="Тони сидит и смотрит в камеру" width="300" height="400">
      <h2>Как играть со мной в канат</h2>
      <ol>
        <li>Покажите мне канат.</li>
        <li>Бросьте его подальше.</li>
        <li>Подождите, пока я принесу его обратно.</li>
        <li>Похвалите меня — и всё сначала!</li>
      </ol>
      <section>
        <h2 id="album">Мой фотоальбом</h2>
        <ul class="gallery">
          <li>
            <figure>
              <img src="../assets/album-puppy.png" alt="Тони-щенок сидит и смотрит в камеру" width="300" height="400">
              <figcaption>Я в детстве</figcaption>
            </figure>
          </li>
          <li>
            <figure>
              <img src="../assets/album-squirrel.png" alt="Тони лежит в обнимку с плюшевой белкой" width="300" height="400">
              <figcaption>С плюшевой белкой</figcaption>
            </figure>
            <span class="badge">Любимая</span>
          </li>
          <li>
            <figure>
              <img src="../assets/album-hoodie.png" alt="Тони стоит в бежевой толстовке с капюшоном" width="300" height="400">
              <figcaption>В худи для холодной погоды</figcaption>
            </figure>
          </li>
          <li>
            <figure>
              <img src="../assets/album-harness.png" alt="Тони в оранжевой шлейке ждёт прогулку" width="300" height="400">
              <figcaption>Шлейка надета — идём гулять!</figcaption>
            </figure>
          </li>
          <li>
            <figure>
              <img src="../assets/album-laugh.png" alt="Тони лежит, запрокинув голову, и широко открыл пасть, будто смеётся" width="300" height="400">
              <figcaption>Смеюсь над шуткой хозяина</figcaption>
            </figure>
          </li>
          <li>
            <figure>
              <img src="../assets/album-sleep.png" alt="Тони спит, положив голову на лапы" width="300" height="319">
              <figcaption>После прогулки</figcaption>
            </figure>
          </li>
        </ul>
      </section>
      <p><a href="../index.html#commands">Какие команды я знаю</a></p>
      <p><a href="../index.html">Вернуться на главную</a></p>
    </main>
    <footer>
      <p><small>&copy; 2026 Тони и его хозяин</small></p>
      <address>Написать хозяину Тони: <a href="mailto:tony@example.com">tony@example.com</a></address>
    </footer>
  </body>
</html>
pages/walks.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Маршрут прогулки Тони: сквер, пруд и поляна для игры с канатом.">
    <title>Где гуляет Тони</title>
    <link rel="stylesheet" href="../css/style.css">
  </head>
  <body>
    <h1>Мой маршрут</h1>
    <ol>
      <li>Сквер у дома.</li>
      <li>Пруд с утками.</li>
      <li>Поляна для игры с канатом.</li>
    </ol>
  </body>
</html>

Продолжите в VS Code

Внесите правила в css/style.css своего my-site. Проверьте широкое и узкое окно. Временно отключите весь блок @supports: подписи должны остаться на однотонной тёмной подложке. Если результат отличается, откройте решение и сравните правила.

Дополнительная задача

Подготовьте три декоративные ленты для упаковки подарков: голубую со светлыми краями и резкой границей посередине, красную с резкой границей и зелёную с шевронами. Сначала сделайте две простые полосы, затем переходите к узору. Цвета можно выбрать свои. Для шевронов используйте процентные остановки двух наложенных повторяющихся градиентов и размер фонового изображения. Проверьте, что между соседними частями узора нет разрывов.

Дополнительное задание: полосы и шевроны

Воспроизведите две полосы с резкой границей и зелёные шевроны по образцу.

Подсказка

Остановки разных цветов в одной позиции дают резкую границу. Шевроны образуют два repeating-linear-gradient() с разными углами и прозрачными участками. Размер и положение фонов настраиваются отдельно.

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Дополнительное задание: полосы и шевроны</title>
  </head>
  <body>
    <p>Голубая лента</p>
    <div class="strip cyan"></div>
    <p>Красная лента</p>
    <div class="strip red"></div>
    <p>Лента с шевронами</p>
    <div class="chevrons"></div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
}
.strip {
    width: 100px;
    height: 50px;
    margin-bottom: 20px;
}
.chevrons {
    height: 100px;
}
.cyan {
    background-image: ???;
}
.red {
    background-image: ???;
}
.chevrons {
    background-color: white;
    background-image: ???;
    background-size: ???;
    background-position: ???;
    background-repeat: repeat-x;
}

История синтаксиса

Почему старые градиенты выглядят иначе

В старых проектах встречаются -webkit-linear-gradient(), -moz-linear-gradient() и -o-linear-gradient(). Префикс здесь относится к функции, а не к background-image. В прежнем синтаксисе слово без to называло начальную сторону: -webkit-linear-gradient(right, red, blue) соответствует linear-gradient(to left, red, blue). Углы старого синтаксиса отсчитывались иначе: для перевода стандартного угла X использовали 90deg - X, например для 135deg — -45deg.

В старых таблицах указаны Safari 5.1–6, Firefox 3.6–15 и Opera 11.1–12 с префиксами. Эти записи нужны для чтения прежнего кода, а не как рекомендация для современных сайтов. Старые фильтры Internet Explorer 6–9 в этом учебнике не используем. Для подбора актуального CSS есть генератор градиентов.