Тег <svg> (от английского scalable vector graphics, «масштабируемая векторная графика») создаёт на странице область, в которой рисуются фигуры, линии и текст по описанию, а не по пикселям. Рисунок не теряет чёткость при любом размере, поэтому тег подходит для значков, логотипов и схем. Тег парный: закрывающий </svg> обязателен. Пустой <svg /> со слэшем браузер тоже принимает, но рисовать в нём нечего.
Внутри svg пишутся свои теги: rect, circle, path, g, text, use и другие. Отдельными страницами в шпаргалке они не описаны, их разбирает раздел «Какие теги пишут внутри svg». Здесь собрано всё, что нужно знать о самом теге: как вставить, как задать размер, зачем нужны viewBox и xmlns, как красить и как сделать значок доступным.
<svg width="120" height="120" viewBox="0 0 120 120">
<circle cx="60" cy="60" r="50" fill="tomato" />
</svg>
В этой статье
- Тег svg в HTML: что это и чем он отличается от других тегов
- Как вставить svg на страницу: прямо в код, img, object и фон
- Размер svg: width, height и значения по умолчанию
- viewBox и preserveAspectRatio: как рисунок подстраивается под область svg
- xmlns в svg: когда он нужен
- Какие теги пишут внутри svg
- Цвет и стили svg: fill, stroke и currentColor
- Тег svg строчный или блочный
- use, symbol и defs: один рисунок много раз
- Доступность svg: title, role и aria-hidden
- Атрибуты тега svg
Тег svg в HTML: что это и чем он отличается от других тегов
Для браузера svg не обычный HTML-элемент. Разбор страницы помещает его и всё вложенное в отдельное пространство имён SVG. У такого элемента свой класс SVGSVGElement, а circle, path и g внутри считаются SVG-тегами со своими правилами.
При разборе страницы регистр названий атрибутов поправляется. Записанный строчными viewbox превращается в viewBox. Если же задать setAttribute("viewbox", ...) из скрипта, название останется строчным и атрибут не сработает.
Вызов document.createElement("svg") создаёт не svg, а неизвестный HTML-элемент. Разметка у него выглядит так же, но рисунка на экране нет. Для svg из скрипта нужен createElementNS с адресом пространства имён.
Внутри svg пустые теги можно закрывать слэшем, <circle /> принимается. В обычном HTML слэш у <div /> ничего не закрывает: следующий div окажется внутри предыдущего.
const NS = "http://www.w3.org/2000/svg";
const bad = document.createElement("svg"); // HTMLUnknownElement
const good = document.createElementNS(NS, "svg"); // SVGSVGElement
Пример: что создаёт браузер
Кнопки создают один и тот же круг тремя способами. Класс и пространство имён измерены в вашем браузере.
Поддерживают все браузеры: по MDN, тег доступен везде с июля 2015 года.
Как вставить svg на страницу: прямо в код, img, object и фон
Один и тот же рисунок подключается четырьмя способами: написать код прямо в странице, подключить файл через img или object, либо взять файл фоном в CSS. От способа зависит, дотянутся ли до фигур внутри стили и скрипты страницы.
<!-- 1. код прямо в странице -->
<svg width="40" height="40" viewBox="0 0 40 40">
<circle cx="20" cy="20" r="16" fill="currentColor" />
</svg>
<!-- 2. файл через img -->
<img src="icon.svg" width="40" height="40" alt="Значок">
<!-- 3. файл через object -->
<object data="icon.svg" type="image/svg+xml" width="40" height="40"></object>
/* 4. файл фоном */
.icon {
width: 40px;
height: 40px;
background: url("icon.svg") no-repeat center / contain;
}
Пример: цвет текста и четыре способа
У всех четырёх рисунков один и тот же код с fill="currentColor". Цвет текста меняется у обёртки.
Сравнение способов
| Способ | Стили страницы доходят до фигур | Цвет через currentColor | Клик по фигуре | Отдельный файл |
|---|---|---|---|---|
| Код в странице | да | да | каждая фигура получает свои события | нет, код записан в странице |
img | нет | нет, цвет из файла | событие получает сам img | да, браузер хранит его в кеше |
object | нет, внутри свой документ | нет, цвет из файла | внутри документа свои события | да |
| Фон в CSS | нет | нет, цвет из файла | событие получает блок с фоном | да |
Значки, которые должны менять цвет, лучше писать прямо в странице. Большие картинки без перекраски удобнее подключать через img: страница остаётся короче, а файл кешируется. Замеры svg через object и img есть на странице object.
Размер svg: width, height и значения по умолчанию
Без размеров и без viewBox svg занимает 300 на 150 пикселей, как canvas. Если задан viewBox, а размеров нет, svg растягивается на всю ширину контейнера, а высоту получает по пропорциям viewBox. Если viewBox нет, а ширина задана в CSS, высота остаётся 150 пикселей.
Атрибуты width и height принимают числа (пиксели, без единиц), проценты и длины с единицами. Если задан viewBox и только один из размеров, второй вычисляется по пропорциям.
<svg>...</svg> <!-- 300 на 150 -->
<svg viewBox="0 0 100 50">...</svg> <!-- по ширине контейнера -->
<svg width="120" height="60">...</svg> <!-- 120 на 60 -->
<svg viewBox="0 0 100 50" width="120">...</svg> <!-- 120 на 60, рисунок растянут -->
Пример: размер при разных атрибутах
Размер svg и масштаб рисунка измерены в рамке. Пунктир показывает границы svg.
viewBox и preserveAspectRatio: как рисунок подстраивается под область svg
Атрибут viewBox задаёт внутреннюю систему координат: четыре числа (x, y, ширина и высота) описывают, какой кусок рисунка виден в области svg. Если пропорции области и viewBox совпадают, масштаб равен ширине области, делённой на ширину viewBox. Меняя viewBox, рисунок приближают, отдаляют и сдвигают, а сами фигуры не трогают.
<svg width="160" height="80" viewBox="0 0 50 25">
...
</svg>
Пример: viewBox как область
Область svg всегда 160 на 80. Меняется только viewBox, а вердикт показывает, какие круги в области видно.
Когда пропорции области и viewBox разные, решает атрибут preserveAspectRatio. Значение по умолчанию xMidYMid meet сохраняет пропорции и показывает рисунок целиком по центру, поэтому сбоку остаётся пустое место. Значение slice закрывает рисунком всю область, лишнее обрезается. Значение none растягивает рисунок по каждой оси отдельно, и круг становится овалом. Без viewBox атрибут не действует.
<svg width="160" height="80" viewBox="0 0 50 50"
preserveAspectRatio="xMidYMid slice">
...
</svg>
Пример: preserveAspectRatio
Квадратный рисунок 50 на 50 помещается в область 160 на 80. Масштаб по осям считает сам браузер.
xmlns в svg: когда он нужен
Атрибут xmlns="http://www.w3.org/2000/svg" объявляет пространство имён. Если svg записан прямо в HTML-страницу, атрибут не нужен: разбор страницы сам понимает, что внутри svg. Он обязателен там, где svg существует отдельным XML-документом: в файле .svg и в адресе data:, который вписывают в img или в CSS-фон. Без него файл не распознаётся как картинка.
В адресе data: есть вторая ловушка: символ # в адресе начинает фрагмент, и остальная часть строки отбрасывается. Поэтому в таком адресе # записывают как %23 или кодируют всю строку, либо берут base64.
.icon {
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Ccircle cx='5' cy='5' r='4' fill='%23c00'/%3E%3C/svg%3E");
}
Пример: когда картинка не загружается
Пять вариантов одного svg. Загрузку проверяет сам кадр.
Какие теги пишут внутри svg
Рисунок собирается из фигур, и каждая фигура пишется своим тегом. Все они работают только внутри svg. По умолчанию у фигур чёрная заливка и нет обводки, поэтому линия line без атрибута stroke на экране не видна.
<svg viewBox="0 0 200 120">
<rect x="10" y="10" width="50" height="30" />
<circle cx="90" cy="25" r="15" />
<line x1="10" y1="80" x2="60" y2="52" stroke="#333" stroke-width="3" />
<path d="M10 112 Q35 88 60 112" fill="none" stroke="#333" />
<text x="90" y="110" font-size="16">svg</text>
</svg>
Пример: добавление тегов по одному
Кнопки включают и выключают теги. Под рисунком показан код последнего нажатого тега.
Основные теги внутри svg
| Тег | Что делает | Главные атрибуты |
|---|---|---|
rect | прямоугольник | x, y, width, height, rx |
circle | круг | cx, cy, r |
ellipse | овал | cx, cy, rx, ry |
line | отрезок | x1, y1, x2, y2, stroke |
polyline, polygon | ломаная и многоугольник | points |
path | произвольная линия или фигура | d |
text | текст | x, y, font-size |
g | группа: общие стили и преобразования для вложенных тегов | fill, stroke, transform |
defs, symbol | описания, которые сами не рисуются | id, viewBox |
use | копия по ссылке | href |
image | картинка внутри svg | href, x, y, width, height |
title, desc | название и описание для программ чтения экрана и подсказки | нет |
style | таблица стилей | нет |
Обычный HTML-тег внутри svg не работает. В примере выше div не остаётся внутри: разбор страницы выносит его наружу, и он выводится после рисунка. Чтобы показать HTML внутри svg, его оборачивают в foreignObject. Для надписей проще взять тег text.
Тег title внутри svg не имеет отношения к тегу title страницы: он задаёт название рисунка. Тег style внутри svg действует на всю страницу, а не только на этот рисунок, подробнее о нём на странице style.
Цвет и стили svg: fill, stroke и currentColor
Свойство fill задаёт заливку, stroke обводку, stroke-width толщину обводки. Их можно писать атрибутом тега или CSS-правилом. Если заданы оба способа, побеждает CSS-правило: атрибут проигрывает любому селектору. Свойства fill и stroke наследуются, поэтому их удобно задавать на g или на самом svg.
Значение currentColor берёт цвет из свойства color родителя. Так один значок меняет цвет вместе с текстом ссылки или кнопки. Всё это работает для svg, записанного в страницу. У файла через img и у фона свои цвета, страница на них не влияет.
.icon { color: seagreen; }
.icon circle { fill: currentColor; }
.icon:hover { color: tomato; }
Пример: откуда берётся цвет
Итоговый fill прочитан браузером: так видно, какой способ победил.
Тег svg строчный или блочный
По умолчанию svg строчный, display равен inline. Он ведёт себя как картинка: стоит в одной строке с текстом и сидит на базовой линии. Под базовой линией браузер резервирует место для нижних хвостов букв, поэтому под svg появляется зазор. Он равен примерно 5 пикселям при шрифте 15 пикселей, точное значение зависит от шрифта и измеряется в примере ниже. Зазор убирают значением display: block или vertical-align: middle. То же самое происходит у img.
svg {
display: block; /* зазора под svg нет, но он больше не стоит в строке */
}
svg.inline-icon {
vertical-align: middle; /* остаётся в строке, зазора нет */
}
Пример: зазор под svg
Красная рамка показывает зазор: высота рамки минус высота svg.
use, symbol и defs: один рисунок много раз
Описание, записанное внутри symbol или defs, само не рисуется. Тег use с ссылкой href="#id" рисует копию. Так собирают спрайт значков: описание записано на странице один раз, а копий может быть сколько угодно, и у каждой свой размер. Цвет задаётся через currentColor.
Спрайт прячут, чтобы он не занимал место. Рисунок из symbol при этом работает и с display: none. Но градиент из спрайта с display: none в проверке не нарисовался. Надёжнее прятать спрайт нулевым размером: width="0" height="0" и position: absolute.
<svg width="0" height="0" style="position:absolute">
<symbol id="star" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01z" />
</symbol>
</svg>
<svg width="24" height="24"><use href="#star" /></svg>
<svg width="64" height="64" style="color:teal"><use href="#star" /></svg>
Пример: три копии одного symbol
Кнопка «Убрать symbol» удаляет описание: копии остаются на странице, но рисовать им нечего.
Доступность svg: title, role и aria-hidden
Программы чтения экрана воспринимают svg как картинку. Если значок только украшает текст рядом, ему добавляют aria-hidden="true", и он пропускается. Если значок несёт смысл, ему дают название: атрибутом aria-label или первым вложенным тегом title, и добавляют role="img", чтобы svg точно считался картинкой.
Для кнопки или ссылки, в которой есть только значок, название нужно кнопке или ссылке: без него она остаётся безымянной. Подробнее о кнопках на странице button.
<button type="button" aria-label="Закрыть">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16">
<path d="M2 2l12 12M14 2L2 14" stroke="currentColor" stroke-width="2" />
</svg>
</button>
Проверка: что видит программа чтения экрана
| Разметка | Что видит программа чтения экрана (по снимку браузера) |
|---|---|
<button><svg>...</svg></button> | кнопка без названия, внутри безымянная картинка |
<button><svg aria-hidden="true">...</svg> Закрыть</button> | кнопка «Закрыть», значок пропущен |
<a><svg role="img" aria-label="Домой">...</svg></a> | ссылка «Домой» |
<a><svg role="img"><title>Поиск</title>...</svg></a> | ссылка «Поиск» |
<p><svg>...</svg></p> | безымянная картинка |
<p><svg><title>Декор</title>...</svg></p> | картинка «Декор»: название взято из title даже без role |
Атрибуты тега svg
Тег принимает глобальные атрибуты (id, class, style, role, aria-*) и собственные, перечисленные в таблице. Обычные свойства вроде fill и stroke тоже можно писать атрибутами, но у них приоритет слабее CSS.
| Атрибут | Что делает | Значение по умолчанию |
|---|---|---|
width, height | размер области svg | auto: 300 на 150 или по viewBox |
viewBox | какая часть рисунка видна в области svg | нет |
preserveAspectRatio | как рисунок вписывается в область с другими пропорциями | xMidYMid meet |
xmlns | пространство имён: нужен в файле .svg и в адресе data:, в HTML не нужен | нет |
x, y | положение вложенного svg; у внешнего svg не действуют | 0 |
version, baseProfile | устарели, на вывод не влияют: MDN советует не использовать в новых проектах | нет |
Полное описание есть в MDN и в спецификации SVG 2.
Частые ошибки с тегом svg
Почти все ошибки с svg от того, что разбирается он не по правилам HTML.
| Ошибка | Что получится | Как исправить |
|---|---|---|
document.createElement("svg") | Неизвестный HTML-элемент, рисунка нет | createElementNS("http://www.w3.org/2000/svg", "svg") |
<div> или <p> прямо внутри svg | Разбор страницы выносит тег наружу, он оказывается после рисунка | Обернуть в foreignObject или взять text |
svg без размеров и без viewBox | Рисунок 300 на 150 пикселей | Задать width и height или viewBox |
Файл или data:-адрес без xmlns | Картинка не загружается | Добавить xmlns="http://www.w3.org/2000/svg" |
Символ # в data:-адресе | Картинка не загружается | Записать как %23 или взять base64 |
Перекрасить значок из img стилями страницы | Цвет не меняется | Записать svg в страницу или задать цвет в самом файле |
setAttribute("viewbox", ...) | Атрибут не срабатывает | Писать viewBox с большой B |
| Зазор под значком в строке | Лишние несколько пикселей под svg | display: block или vertical-align: middle |
| Кнопка только со значком, без названия | Программа чтения экрана видит безымянную кнопку | aria-label у кнопки и aria-hidden="true" у значка |