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

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

В предыдущем уроке переход шёл вдоль прямой. У радиального градиента цвета расходятся от выбранного центра по кругу или эллипсу. Как и линейный градиент, это изображение для background-image или background, а не цвет для background-color. Оно не меняет форму самого элемента.

Круг и эллипс

CSS
.card {
  background-color: orange;
  background-image: radial-gradient(orange, black);
}

Функция radial-gradient() принимает минимум две цветовые остановки. Без дополнительных настроек используются форма ellipse, центр 50% 50% и размер farthest-corner. Первый цвет начинается в центре, последний достигается на конечной окружности или эллипсе; дальше продолжается последний цвет. Цвета между ними плавно переходят друг в друга, а не обязательно делят площадь на равные части.

circle задаёт круговую форму. На прямоугольнике отличие от ellipse хорошо видно; на квадрате при центральном положении и размере по умолчанию они совпадают. Для цветов подходят имена, HEX, RGB и HSL, в том числе с прозрачностью — как в уроке о цвете.

Четыре градиента: круг и эллипс: попробуйте сами

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 test"></div>
        <figcaption>green, red</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>ellipse, orange, black</figcaption>
      </figure>
      <figure>
        <div class="sample test3"></div>
        <figcaption>circle, green, red</figcaption>
      </figure>
      <figure>
        <div class="sample test4"></div>
        <figcaption>circle, orange, black</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 250px;
    height: 125px;
}
.test {
    background-image: radial-gradient(green, red);
}
.test2 {
    background-image: radial-gradient(ellipse, orange, black);
}
.test3 {
    background-image: radial-gradient(circle, green, red);
}
.test4 {
    background-image: radial-gradient(circle, orange, black);
}

Положение центра

CSS
background-image: radial-gradient(circle at 30% 40%, red, blue);

Ключевое слово at вводит положение центра. В паре чисел сначала идёт горизонтальная координата, затем вертикальная: здесь 30% ширины и 40% высоты области фона от её левого верхнего угла. Подходят px, em, rem, проценты и ключевые слова: left, center, right по горизонтали; top, center, bottom по вертикали. Два слова разных осей можно переставить: top left и left top равносильны.

Синтаксис позиции похож на background-position, но задачи разные: at передвигает центр внутри изображения градиента, а свойство передвигает фоновое изображение целиком. Это особенно заметно, когда задан background-size.

Шесть положений центра: попробуйте сами

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 test"></div>
        <figcaption>at 5px 45px</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>at 50% top</figcaption>
      </figure>
      <figure>
        <div class="sample test3"></div>
        <figcaption>circle at top left</figcaption>
      </figure>
      <figure>
        <div class="sample test4"></div>
        <figcaption>ellipse at 0% bottom</figcaption>
      </figure>
      <figure>
        <div class="sample test5"></div>
        <figcaption>circle at center bottom</figcaption>
      </figure>
      <figure>
        <div class="sample test6"></div>
        <figcaption>at 100% 100%</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 125px;
    height: 125px;
}
.test {
    background-image: radial-gradient(at 5px 45px, green, black);
}
.test2 {
    background-image: radial-gradient(at 50% top, green, black);
}
.test3 {
    background-image: radial-gradient(circle at top left, green, black);
}
.test4 {
    background-image: radial-gradient(ellipse at 0% bottom, green, black);
}
.test5 {
    background-image: radial-gradient(circle at center bottom, green, black);
}
.test6 {
    background-image: radial-gradient(at 100% 100%, green, black);
}

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

CSS
background-image: radial-gradient(circle, red 10%, green 50%, blue);

Позиции отсчитываются от центра наружу. Для круга 100% соответствует конечному радиусу; у эллипса — горизонтальному радиусу, а в других направлениях цветовые контуры растягиваются по форме эллипса. Процент остановки не равен проценту площади. Применяются и единицы длины; у эллипса они также отсчитываются вдоль горизонтального радиуса.

В примере красный остаётся однотонным до 10%, затем переходит в зелёный к 50%, потом в синий к 100%. Пропущенные крайние позиции становятся 0% и 100%, промежуточные распределяются между ближайшими заданными. Поэтому red 10%, green — красный центр и плавный переход к зелёному, а не две однотонные области.

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

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 test"></div>
        <figcaption>circle: red 10%, green</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>circle: red 10%, green 50%, blue</figcaption>
      </figure>
      <figure>
        <div class="sample test3"></div>
        <figcaption>circle: red 50%, green 85%, blue</figcaption>
      </figure>
      <figure>
        <div class="sample test4"></div>
        <figcaption>ellipse: red 10%, green</figcaption>
      </figure>
      <figure>
        <div class="sample test5"></div>
        <figcaption>ellipse: red 10%, green 50%, blue</figcaption>
      </figure>
      <figure>
        <div class="sample test6"></div>
        <figcaption>ellipse: red 50%, green 85%, blue</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 200px;
    height: 125px;
}
.test {
    background-image: radial-gradient(circle, red 10%, green);
}
.test2 {
    background-image: radial-gradient(circle, red 10%, green 50%, blue);
}
.test3 {
    background-image: radial-gradient(circle, red 50%, green 85%, blue);
}
.test4 {
    background-image: radial-gradient(red 10%, green);
}
.test5 {
    background-image: radial-gradient(red 10%, green 50%, blue);
}
.test6 {
    background-image: radial-gradient(red 50%, green 85%, blue);
}

Размер по ключевым словам

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

Четыре способа задать конечный размер
Значение Круг Эллипс
closest-side Радиус до ближайшей стороны. Горизонтальный и вертикальный радиусы до ближайшей стороны по каждой оси.
farthest-side Радиус до самой дальней стороны. Радиусы до самых дальних сторон по каждой оси.
closest-corner Радиус до ближайшего угла. Проходит через ближайший угол; пропорции как у closest-side.
farthest-corner Радиус до самого дальнего угла. Проходит через самый дальний угол; пропорции как у farthest-side. Значение по умолчанию.

Четыре размера для круга и эллипса: попробуйте сами

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 sample-1a"></div>
        <figcaption>circle closest-side at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-1b"></div>
        <figcaption>ellipse closest-side at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-2a"></div>
        <figcaption>circle closest-corner at 60% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-2b"></div>
        <figcaption>ellipse closest-corner at 60% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-3a"></div>
        <figcaption>circle farthest-side at 100% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-3b"></div>
        <figcaption>ellipse farthest-side at 100% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-4a"></div>
        <figcaption>circle farthest-corner at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-4b"></div>
        <figcaption>ellipse farthest-corner at 60% 60%</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 250px;
    height: 125px;
}
.sample-1a {
    background-image: radial-gradient(circle closest-side at 60% 60%, plum, black, orange);
}

.sample-1b {
    background-image: radial-gradient(ellipse closest-side at 60% 60%, plum, black, orange);
}

.sample-2a {
    background-image: radial-gradient(circle closest-corner at 60% 50%, plum, black, orange);
}

.sample-2b {
    background-image: radial-gradient(ellipse closest-corner at 60% 50%, plum, black, orange);
}

.sample-3a {
    background-image: radial-gradient(circle farthest-side at 100% 50%, plum, black, orange);
}

.sample-3b {
    background-image: radial-gradient(ellipse farthest-side at 100% 50%, plum, black, orange);
}

.sample-4a {
    background-image: radial-gradient(circle farthest-corner at 60% 60%, plum, black, orange);
}

.sample-4b {
    background-image: radial-gradient(ellipse farthest-corner at 60% 60%, plum, black, orange);
}

Явные радиусы

CSS
background-image: radial-gradient(50px, red, blue);
background-image: radial-gradient(15% 45%, red, blue);
background-image: radial-gradient(100px 1em, red, blue);

Одно значение длины задаёт радиус круга, два — горизонтальный и вертикальный радиусы эллипса. Это радиусы, не диаметры. Для круга одно процентное значение недопустимо: circle 50% не работает. Для эллипса проценты разрешены: горизонтальный радиус считается от ширины области фона, вертикальный — от высоты. Здесь 15% 45% в блоке 200×200 — радиусы 30px и 90px. Отрицательные радиусы недопустимы.

Три способа задать радиусы: попробуйте сами

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 test"></div>
        <figcaption>50px</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>15% 45%</figcaption>
      </figure>
      <figure>
        <div class="sample test3"></div>
        <figcaption>100px 1em</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 200px;
    height: 200px;
}
.test {
    background-image: radial-gradient(50px, red, orange, yellow, green, skyblue, blue, violet);
}
.test2 {
    background-image: radial-gradient(15% 45%, red, orange, yellow, green, skyblue, blue, violet, white);
}
.test3 {
    background-image: radial-gradient(100px 1em, orange, yellow, green, skyblue, blue, violet);
}

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

Функция repeating-radial-gradient() использует те же форму, центр, размер и остановки, но повторяет цветовую последовательность кольцами. Период равен разности последней и первой позиции: orange 5%, green 10%, skyblue 15% повторяется через 10% радиуса. Это три цвета, а не четыре.

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

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 test"></div>
        <figcaption>orange 5%, green 10%, skyblue 15%</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>10px 2em at 40% 40%</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 290px;
    height: 290px;
}
.test {
    background-image: repeating-radial-gradient(orange 5%, green 10%, skyblue 15%);
}
.test2 {
    background-image: repeating-radial-gradient(10px 2em at 40% 40%, orange 5%, green 10%, skyblue 15%);
}

Во втором примере горизонтальный радиус равен 10px, вертикальный — 2em, центр находится на 40% по обеим осям. Остановки по-прежнему заданы в процентах: период по горизонтали равен 1px, а по вертикали — 0.2em. Маленький период может сливаться на экране; увеличьте радиусы, чтобы увидеть отдельные кольца.

Размер повторяющегося градиента

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

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

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 sample-1a"></div>
        <figcaption>circle closest-side at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-1b"></div>
        <figcaption>ellipse closest-side at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-2a"></div>
        <figcaption>circle closest-corner at 60% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-2b"></div>
        <figcaption>ellipse closest-corner at 60% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-3a"></div>
        <figcaption>circle farthest-side at 100% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-3b"></div>
        <figcaption>ellipse farthest-side at 100% 50%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-4a"></div>
        <figcaption>circle farthest-corner at 60% 60%</figcaption>
      </figure>
      <figure>
        <div class="sample sample-4b"></div>
        <figcaption>ellipse farthest-corner at 60% 60%</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 250px;
    height: 125px;
}
.sample-1a {
    background-image: repeating-radial-gradient(circle closest-side at 60% 60%, plum, black, orange);
}

.sample-1b {
    background-image: repeating-radial-gradient(ellipse closest-side at 60% 60%, plum, black, orange);
}

.sample-2a {
    background-image: repeating-radial-gradient(circle closest-corner at 60% 50%, plum, black, orange);
}

.sample-2b {
    background-image: repeating-radial-gradient(ellipse closest-corner at 60% 50%, plum, black, orange);
}

.sample-3a {
    background-image: repeating-radial-gradient(circle farthest-side at 100% 50%, plum, black, orange);
}

.sample-3b {
    background-image: repeating-radial-gradient(ellipse farthest-side at 100% 50%, plum, black, orange);
}

.sample-4a {
    background-image: repeating-radial-gradient(circle farthest-corner at 60% 60%, plum, black, orange);
}

.sample-4b {
    background-image: repeating-radial-gradient(ellipse farthest-corner at 60% 60%, plum, black, orange);
}

Однотонные участки и резкие границы

Повтор одного цвета на двух позициях создаёт однотонный участок: black 0, black 2px, white 4px, white 6px. Между 2px и 4px остаётся плавный переход. Это четыре остановки двух цветов. В цветном примере — четырнадцать остановок семи цветов, а не четырнадцать разных цветов.

Для действительно резкой границы разные цвета должны встретиться на одной позиции: black 0 10px, white 10px 20px. Если первый и последний цвета периода различаются, на стыке тоже будет скачок; одинаковые крайние цвета убирают скачок на стыке, но сами по себе не устраняют другие резкие границы. border-radius в примере скругляет сам блок независимо от формы градиента, как в уроке о границах.

Сравнение однотонных участков и резких колец: попробуйте сами

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 test"></div>
        <figcaption>Семь цветов, четырнадцать остановок</figcaption>
      </figure>
      <figure>
        <div class="sample test2"></div>
        <figcaption>Два цвета, четыре остановки</figcaption>
      </figure>
      <figure>
        <div class="sample hard"></div>
        <figcaption>Резкие границы: black 0 10px, white 10px 20px</figcaption>
      </figure>
    </div>
  </body>
</html>
style.css
CSS
body {
    margin: 1rem;
    font-family: system-ui, sans-serif;
}
.samples {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}
figure {
    margin: 0;
    max-width: 100%;
}
figcaption {
    max-width: 250px;
    margin-top: 0.3rem;
}
.sample {
    max-width: 100%;
}
.sample {
    width: 290px;
    height: 290px;
}
.test {
    background-image: repeating-radial-gradient(
        red,
        red 5px,
        orange 10px,
        orange 15px,
        yellow 20px,
        yellow 25px,
        green 30px,
        green 35px,
        SkyBlue 40px,
        SkyBlue 45px,
        blue 50px,
        blue 55px,
        violet 60px,
        violet 65px
    );
}
.test2 {
    background-image: repeating-radial-gradient(black, black 2px, white 4px, white 6px);
}
.sample {
    border-radius: 100%;
}
.hard {
    background-image: repeating-radial-gradient(circle, black 0 10px, white 10px 20px);
}

Подсветка вводного блока

Перемещайте центр светлого пятна, меняйте форму и непрозрачность. Наблюдайте, как круг и эллипс ведут себя на прямоугольнике. Код задаёт фон; цвет текста остаётся обычным тёмным.

Привет! Меня зовут Тони

Люблю прогулки, игрушки и тёплые солнечные пятна.

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

Радиальная подсветка

background-image: radial-gradient(ellipse at 80% 20%, rgb(255 196 110 / 0.45) 0%, rgb(255 196 110 / 0) 65%);
background-color: #fff4e8;

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

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

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

1. Каковы настройки radial-gradient(red, blue) по умолчанию?
Показать ответ

ellipse, центр 50% 50%, farthest-corner — Это эллипс от центра до самого дальнего угла.

2. Что означает at 30% 40%?
Показать ответ

Центр на 30% ширины и 40% высоты области фона — Первое число задаёт горизонтальную координату, второе — вертикальную.

3. Что означает остановка 50% для круга?
Показать ответ

Половину конечного радиуса — Это расстояние от центра, а не доля площади.

4. Как рассчитывается ellipse farthest-side?
Показать ответ

По самым дальним сторонам отдельно по каждой оси — Горизонтальный и вертикальный радиусы определяются независимо.

5. Какая запись явного размера недопустима?
Показать ответ

radial-gradient(circle 50%, red, blue) — Один процентный радиус для круга недопустим; два процента для эллипса допустимы.

6. Каков период orange 5%, green 10%, skyblue 15%?
Показать ответ

10% конечного радиуса — Период равен 15% − 5%; у эллипса по горизонтали используется горизонтальный радиус.

7. Есть ли плавный переход в black 0, black 2px, white 4px, white 6px?
Показать ответ

Да, между 2px и 4px — Однотонные участки сами по себе не создают резкую границу между разными цветами.

8. Как сохранить лапу поверх радиальной подсветки?
Показать ответ

Перечислить URL лапы первым, градиент вторым — Первое фоновое изображение рисуется сверху; позиции и размеры нужно задать для обоих.

Задание

Ожидаемый результат: за приветствием появится мягкое тёплое пятно. Прозрачный край открывает прежний фон; рисунок лапы остаётся сверху. Не используйте сокращённый background без необходимости: он может сбросить размеры и позиции остальных слоёв. При необходимости повторите урок о фоне и урок о тенях элементов.

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

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%);
}

/* Фигура обрезает только своё содержимое, тень родительской карточки остаётся снаружи. */
.gallery figure {
  display: grid;
  position: relative;
  overflow: hidden;
  border-radius: 12px;
}

/* Изображение и подпись занимают одну ячейку; длинный текст увеличивает её высоту. */
.gallery figure > img {
  grid-area: 1 / 1;
  align-self: stretch;
  height: 100%;
  object-fit: cover;
}

.gallery figcaption {
  grid-area: 1 / 1;
  align-self: end;
  z-index: 1;
  padding: 2.5rem 0.5rem 0.5rem;
}

/* Запасная однотонная подложка остаётся в прежнем правиле; градиент подключается условно. */
@supports (background-image: linear-gradient(black, white)) {
  .gallery figcaption {
    background-color: transparent;
    background-image: linear-gradient(
      to top,
      rgb(57 35 19 / 95%) 0,
      rgb(57 35 19 / 95%) calc(100% - 2rem),
      rgb(57 35 19 / 0%) 100%
    );
  }
}
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. Проверьте главную страницу и фотоальбом, положение лапы, перенос текста и запасной фон. Если что-то отличается, сравните CSS с решением в редакторе.

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

Создайте узор для обёрточной бумаги: заполните страницу оранжево-жёлтыми концентрическими кольцами. Используйте repeating-radial-gradient() и background-size 60px 60px. Кольца повторяются внутри каждого изображения, а background-repeat повторяет эти изображения по странице. Цвета можно выбрать свои. Сначала повторите образец, затем измените размер плитки на 90px 90px: сравните расстояние между центрами узора и толщину колец. Объясните, какую настройку нужно изменить, чтобы сделать сами кольца толще.

Дополнительное задание: узор из колец

Соберите повторяющийся узор из оранжевых и жёлтых колец, как в образце.

Подсказка

Используйте repeating-radial-gradient(circle, …). Остановки задают толщину колец, а background-size: 60px 60px — размер повторяемой плитки.

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Задание 30: узор из радиальных градиентов</title>
  </head>
  <body>
    <main aria-label="Узор из повторяющихся радиальных градиентов"></main>
  </body>
</html>
style.css
CSS
body {
    margin: 0;
    min-height: 100vh;
    background-color: orangered;
}
body {
    background-image: ???;
    background-size: ???;
}

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

Префиксы в старых проектах

Старые версии Chrome и Safari использовали -webkit-radial-gradient(), Firefox — -moz-radial-gradient(), Opera — -o-radial-gradient(). В старом синтаксисе позицию могли писать без at перед формой: -webkit-radial-gradient(center, circle, red, blue). В стандартной записи: radial-gradient(circle at center, red, blue). Префикс относится к функции, а не свойству.

Прежние таблицы отмечали стандартную запись с Chrome 26, Firefox 16, Opera 12.1, Safari 6.1 и Internet Explorer 10. Это исторические сведения для чтения старого кода. В новых примерах используем стандартные функции; для самостоятельных экспериментов есть генератор градиентов.