Наклонная жёсткая граница в градиенте на многих экранах выглядит рвано, «лесенкой». Слева — как получается по умолчанию, справа — с маленькой поправкой:

/* граница-«лесенка» */
.box--hard {
  background: linear-gradient(30deg, #e03131 50%, #1971c2 50%);
}

/* граница сглажена: переход шириной 1px */
.box--soft {
  background: linear-gradient(30deg, #e03131 calc(50% - 0.5px), #1971c2 calc(50% + 0.5px));
}

Откуда берётся лесенка

У векторной фигуры (SVG или clip-path) браузер сглаживает край: пиксели на границе получают промежуточный цвет. Жёсткую границу в градиенте он так не обрабатывает — каждый пиксель окрашивается либо в один цвет, либо в другой, и на наклонной линии видны ступеньки.

Лечение — дать границе крошечный плавный переход вручную: развести два стопа на 1px (по полпикселя в каждую сторону). Глазу это незаметно, а ступеньки пропадают. Хватит и сдвига одного стопа: #e03131 calc(50% - 1px), #1971c2 50%.

Горизонтальным и вертикальным границам поправка не нужна: там пиксели совпадают с линией.

Три элемента вместо одного

«Флаг из одного элемента» — немного лукавая формулировка. У каждого элемента, кроме пустых вроде <img> и <input>, есть два псевдоэлемента — ::before и ::after. Их можно стилизовать отдельно, и они дают то, чего нет у фона:

  • поворот — фоновый слой нельзя повернуть, а псевдоэлемент можно через rotate;
  • сложную форму — border-radius, clip-path, маски;
  • гладкий край — обрезанная фигура сглаживается браузером, в отличие от границы в градиенте.

Два обязательных условия: свойство content (хотя бы пустая строка, иначе псевдоэлемент не появится) и position: absolute относительно флага. Для этого в заготовке из первой главы и стоит position: relative.

Зубцы через clip-path

У флага Бахрейна такой же зубчатый край, как у Катара из главы 5, но зубцов пять. Вместо плитки из конических градиентов возьмём белый ::before на весь флаг и обрежем его многоугольником:

.flag--bahrain {
  aspect-ratio: 5 / 3;
  background: #ce1126;
}

.flag--bahrain::before {
  content: "";
  position: absolute;
  inset: 0;
  background: #fff;
  clip-path: polygon(
    0 0, 25% 0,
    40% 10%, 25% 20%, 40% 30%, 25% 40%, 40% 50%,
    25% 60%, 40% 70%, 25% 80%, 40% 90%, 25% 100%,
    0 100%
  );
}

Точки многоугольника идут по контуру белой области: верхний левый угол, затем зигзаг между 25% и 40% ширины, затем нижний левый угол. Края зубцов получаются гладкими без всяких поправок. Тот же приём подходит для треугольников у Чехии и Багам: треугольник из clip-path всегда ровнее конического. Подробно о функциях polygon(), circle() и других — в статье про clip-path.

Звёзды через ::before и ::after

Флаг Панамы — четыре четверти (конический градиент) и две звезды разного цвета. Звёзды — по одной на каждый псевдоэлемент:

.flag--panama {
  --red: #da121a;
  --blue: #072357;
  aspect-ratio: 3 / 2;
  background: conic-gradient(var(--red) 25%, #fff 0 50%, var(--blue) 0 75%, #fff 0);
}

.flag--panama::before,
.flag--panama::after {
  content: "";
  position: absolute;
  top: 25%;
  left: 25%;
  width: 18%;
  aspect-ratio: 1;
  translate: -50% -50%;
  background: var(--blue);
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

.flag--panama::after {
  top: 75%;
  left: 75%;
  background: var(--red);
}

Общие свойства звёзд описаны один раз для обоих псевдоэлементов, а ::after переопределяет только позицию и цвет. translate: -50% -50% ставит центр звезды точно в центр своей четверти.

Кто-то считает clip-path в CSS-рисунках «читерством» — звезду можно собрать и из конических градиентов. Но в рабочем коде важнее читаемость, а clip-path и маски пригодятся далеко за пределами флагов. Другие фигуры на чистом CSS — в статье о CSS-фигурах.

Где граница разумного

Гербы, эмблемы, надписи и мелкие детали на CSS рисовать можно, но не нужно: код раздувается, а результат хуже, чем у SVG-файла. Разумная граница — полосы, кресты, круги, треугольники и простые фигуры. Всё сложнее — в SVG.

Флаг должен быть виден не только глазам

Пустой <span> с фоном скринридер просто пропустит: для него там ничего нет. Если флаг несёт смысл (страна команды, язык интерфейса), ему нужна текстовая альтернатива:

<span class="flag flag--finland" role="img" aria-label="Флаг Финляндии"></span>

role="img" говорит, что элемент — изображение, а aria-label — это его alt-текст. Если же рядом и так написано название страны, флаг декоративный: лучше скрыть его от скринридера через aria-hidden="true", чтобы название не прозвучало дважды.

Практика

Лучший способ закрепить градиенты — нарисовать несколько флагов самостоятельно. Список примерно по возрастанию сложности:

  1. Монако — две полосы.
  2. Эстония — три полосы.
  3. Объединённые Арабские Эмираты — полосы плюс вертикальная полоса у древка.
  4. Лаос — полосы разной ширины и круг.
  5. Исландия — крест с окантовкой.
  6. Доминиканская Республика (без герба) — крест и четыре цветных прямоугольника.
  7. Сент-Люсия — треугольники друг на друге.

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