31 Модуль 7 Графика и эффекты 17 мин урок 6 из 25

SVG в вёрстке

Иконка, которая не мылится, красится из CSS и весит меньше килобайта. Разбираем SVG изнутри: viewBox, спрайты, currentColor.

Что ты научишься делать

  • Прочитаешь SVG как обычную разметку и поймёшь, что в нём что
  • Соберёшь иконочный спрайт и покрасишь иконки из CSS
  • Выберешь способ вставки под задачу: инлайн, img или фон

#Это не картинка, это разметка

PNG и JPG — сетка пикселей. Увеличил вдвое — получил кашу. SVG устроен иначе: это текстовый файл с описанием фигур. Браузер каждый раз рисует его заново под нужный размер.

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

Интерактивный разбор доступен при включённом JavaScript.

#viewBox — главное, что нужно понять

viewBox="0 0 24 24" означает: «внутри этой картинки система координат от 0 до 24 по обеим осям». Это не пиксели, а условные единицы.

А сколько места картинка займёт на странице, решают width, height или CSS. Одна и та же иконка с viewBox="0 0 24 24" может быть 16 или 160 пикселей — содержимое просто отмасштабируется.

Так не надо

<svg width="24" height="24">
  <path d="M4 12h16"/>
</svg>

Без viewBox координаты жёстко привязаны к пикселям. Задашь в CSS другой размер — линия останется на месте, а холст обрежется.

Так надо

<svg viewBox="0 0 24 24" width="24" height="24">
  <path d="M4 12h16"/>
</svg>

С viewBox содержимое масштабируется вместе с картинкой. Атрибуты размера при этом полезны: они резервируют место до загрузки.

#Три способа вставки

СпособКрасится из CSSКэшируетсяКогда брать
Инлайн в HTMLДа, полностьюНет — едет с каждой страницейИконки, которые меняют цвет
<img src="i.svg">НетДа, отдельным файломИллюстрации и логотипы одного цвета
background-imageНетДаДекор, маркеры списков, фоны
Спрайт + <use>ДаДа, один файл на все иконкиНабор иконок в интерфейсе

Главный водораздел — нужно ли красить иконку из CSS. Внутрь <img> и фона стили страницы не проникают: файл рисуется в изоляции.

#currentColor — почему иконки красятся сами

Ключевое слово currentColor означает «текущий цвет текста». Если в иконке написать stroke="currentColor", она унаследует цвет от родителя — и не нужно ни одного отдельного правила.

Одна иконка, четыре цвета
В песочницу

#Спрайт: один файл на все иконки

Инлайнить одну и ту же иконку двадцать раз — значит двадцать раз повторить её код. Спрайт решает это: все иконки описываются один раз внутри <symbol>, а в местах использования остаётся короткая ссылка.

HTMLСпрайт в начале body
<svg width="0" height="0" aria-hidden="true" style="position:absolute">  <symbol id="i-check" viewBox="0 0 24 24">    <path d="m5 12.5 4.5 4.5L19 7"/>  </symbol>  <symbol id="i-cross" viewBox="0 0 24 24">    <path d="m6 6 12 12M18 6 6 18"/>  </symbol></svg><!-- Дальше по странице — только ссылки --><svg class="icon"><use href="#i-check"/></svg><svg class="icon"><use href="#i-cross"/></svg>

viewBox задаётся у <symbol>, а не у места использования. Размер задаётся снаружи — обычно классом.

#Что выкинуть из экспорта

Файл, выгруженный из редактора, обычно вдвое-втрое больше нужного. Редактор кладёт в него служебные данные, которые браузеру не нужны.

Что удалитьПочему
<?xml ... ?> и <!DOCTYPE>Нужны отдельному файлу, но не инлайновому SVG
<title> вроде «Frame 12»Имя слоя из редактора. Читалка озвучит его вслух
xmlns:figma, data-name, id="Layer_1"Служебные атрибуты редактора
Атрибуты fill на каждом путиМешают покрасить иконку из CSS
Группы <g> без трансформацийПустая вложенность
Числа вида 12.000000001Двух знаков после запятой хватает с запасом

#SVG в CSS без отдельного файла

Маленькую графику можно положить прямо в стили — как data-URI. Это экономит запрос и удобно для декоративных мелочей: галочки в списке, стрелки у выпадающего списка, узора на фоне.

Важная тонкость: в url() нужно экранировать решётку и кавычки, иначе браузер разберёт значение неправильно.

CSSlist.css
.checklist li {  padding-inline-start: 28px;  background-image: url('data:image/svg+xml;utf8,        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"         fill="none" stroke="%230f8f6a" stroke-width="2.5"><path d="m5 12.5 4.5 4.5L19 7"/></svg>');  background-repeat: no-repeat;  background-size: 18px;  background-position: 0 .2em;}

#Доступность SVG

Иконки бывают двух видов, и обращаться с ними надо по-разному.

Декоративная — рядом есть текст, иконка просто дублирует смысл. Её прячут: aria-hidden="true".

Смысловая — иконка сама по себе несёт информацию, например кнопка-крестик без подписи. Ей нужно имя.

HTMLДва случая
<!-- Рядом есть текст: иконку прячем от читалки --><button type="button">  <svg class="icon" aria-hidden="true"><use href="#i-check"/></svg>  Сохранить</button><!-- Текста нет: имя даём кнопке, а не картинке --><button type="button" aria-label="Закрыть окно">  <svg class="icon" aria-hidden="true"><use href="#i-cross"/></svg></button><!-- Самостоятельная иллюстрация: роль и заголовок на самом svg --><svg role="img" aria-labelledby="chart-title" viewBox="0 0 200 120">  <title id="chart-title">Продажи выросли вдвое за квартал</title>  <path d="M10 100 L60 70 L120 40 L190 20"/></svg>

#Как это выглядит в реальном проекте

CSSicons.css
@layer components {  .icon {    /* Размер по шрифту рядом: иконка растёт вместе с текстом */    inline-size: 1.25em;    block-size: 1.25em;    flex: none;    fill: none;    stroke: currentColor;    stroke-width: 1.7;    stroke-linecap: round;    stroke-linejoin: round;    /* Оптическая посадка на строку: иконки кажутся выше текста */    vertical-align: -0.16em;  }  .icon--sm { inline-size: 1em; block-size: 1em; }  .icon--lg { inline-size: 1.75em; block-size: 1.75em; }  /* Заливочные иконки — отдельным модификатором */  .icon--solid { fill: currentColor; stroke: none; }}

#Когда НЕ надо

  • Фотографии. SVG описывает фигуры. Попытка сохранить в него фото даёт файл на несколько мегабайт — это делают только автоматические трассировщики, и результат всегда хуже WebP.
  • Сложные иллюстрации инлайном. Карта страны с тысячей контуров в разметке раздувает HTML и тормозит первый рендер. Такое подключают через img.
  • Иконочные шрифты «зато привычно». Иконочный шрифт ломается при отключённых шрифтах, требует псевдоэлементов и не даёт многоцветности. Спрайт лучше по всем пунктам.
  • Инлайн одинаковых иконок. Двадцать копий одной иконки в разметке — это двадцать копий её кода. Для повторяющихся нужен спрайт.

Где спотыкаются почти все

Забыт viewBox

Почему Иконка перестаёт масштабироваться: при смене размера содержимое остаётся прежним, а холст обрезается.

Как надо Всегда указывай viewBox. Размер задавай отдельно, в CSS.

На путях остались атрибуты fill из редактора

Почему Атрибут в разметке перебивает наследование, и currentColor перестаёт работать: иконка остаётся чёрной.

Как надо Вычисти fill из путей и задай его один раз в CSS через currentColor.

Одинаковые id внутри нескольких инлайновых SVG

Почему Градиенты и маски ищутся по id на всей странице. Два id="grad" — и вторая иконка возьмёт градиент первой.

Как надо Делай id уникальными или используй спрайт, где каждая иконка описана один раз.

Название слоя из редактора осталось в title

Почему Программа чтения с экрана произнесёт «Frame 12» вместо осмысленного описания.

Как надо Удали служебный title. Декоративной иконке — aria-hidden="true".

Векторная графика SVG
Изображение, описанное фигурами и координатами, а не пикселями.
Область просмотра viewBox
Система координат внутри SVG. Не связана с размером на странице.
Спрайт sprite
Один файл с набором symbol, на которые ссылаются через use.
Текущий цвет currentColor
Ключевое слово, подставляющее значение color родителя.
Контур path
Универсальная фигура, описанная командами в атрибуте d.

Проверь себя

  1. Что задаёт <code>viewBox="0 0 24 24"</code>?

    Это условные единицы. Размер на странице задают <code>width</code>, <code>height</code> или CSS.

  2. Почему иконка, вставленная через <code>&lt;img src="icon.svg"&gt;</code>, не красится из CSS?

    Внутри <code>img</code> — отдельный документ. Красить из CSS можно только инлайновый SVG или спрайт через <code>use</code>.

  3. Иконка осталась чёрной, хотя в CSS написано <code>stroke: currentColor</code>. Что вероятнее всего?

    Атрибут в разметке перебивает наследование. Вычисти <code>fill</code> и <code>stroke</code> из путей.

  4. Иконка стоит внутри кнопки рядом с текстом «Сохранить». Что с ней делать для доступности?

    Текст уже всё сказал. Иконка дублирует смысл — её прячут, чтобы читалка не повторяла дважды.

  5. Для чего SVG подходит плохо?

    SVG описывает фигуры. Фотография — это миллионы пикселей, ей нужен растровый формат вроде WebP.

Задание

Собрать иконочный спрайт

В разметке три раза повторяется один и тот же код иконки. Собери спрайт: опиши каждую иконку один раз внутри <symbol> с уникальным id, а в кнопках оставь <svg class="icon"><use href="#id"/></svg>.

В CSS задай иконкам stroke: currentColor, чтобы они красились вместе с текстом кнопки. Декоративным иконкам не забудь aria-hidden.

Открыть в песочнице
    Подсказка 1

    Спрайт кладётся один раз в начало: <svg width="0" height="0" style="position:absolute"><symbol id="i-check" viewBox="0 0 24 24">…</symbol></svg>

    Подсказка 2

    В кнопке остаётся: <svg class="icon" aria-hidden="true"><use href="#i-check"/></svg>

    Подсказка 3

    В CSS: .icon { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }

    Показать решение
    HTMLindex.html
    <svg width="0" height="0" aria-hidden="true" style="position:absolute">  <symbol id="i-check" viewBox="0 0 24 24">    <path d="m5 12.5 4.5 4.5L19 7"/>  </symbol>  <symbol id="i-cross" viewBox="0 0 24 24">    <path d="m6 6 12 12M18 6 6 18"/>  </symbol></svg><button class="btn btn--ok">  <svg class="icon" aria-hidden="true"><use href="#i-check"/></svg>  Сохранить</button><button class="btn btn--bad">  <svg class="icon" aria-hidden="true"><use href="#i-cross"/></svg>  Удалить</button>
    CSSstyle.css
    .icon {  width: 18px;  height: 18px;  flex: none;  fill: none;  stroke: currentColor;  stroke-width: 2;  stroke-linecap: round;  stroke-linejoin: round;}.btn {  display: inline-flex;  align-items: center;  gap: 8px;  padding: 9px 16px;  font: inherit;  font-weight: 600;  background: #f4f4f8;  border: 0;  border-radius: 999px;}.btn--ok { color: #0f8f6a; }.btn--bad { color: #c8394a; }

    Проверь себя