Ви публікуєте документацію або внутрішній портал. На вашому ноутбуці все виглядає добре. Потім хтось відкриває сайт у корпоративному браузері з жорстким CSP, відключеними кастомними шрифтами та в темному режимі. Раптом ваші «корисні» блоки-сповіщення перетворюються на порожні квадрати, іконки зміщуються, а порушення контрасту дратують юридичний відділ.
Блоки-сповіщення — це несподівано складний компонент: тут перетинаються інтерфейс, бренд, доступність і політика безпеки під час рендерингу. Чисте рішення — вбудоване SVG + CSS-змінні. Жодних шрифтів-іконок. Жодних загадкових гліфів. Жодного надіяння, що шрифт завантажиться до того, як користувачі втратять терпіння.
Зміст
- Чому варто припинити використовувати шрифти-іконки
- Факти та трохи історії (корисно, не ностальгічно)
- Цілі дизайну: що означає «хороші» блоки-сповіщення
- Референсний компонент: HTML + вбудоване SVG + CSS-змінні
- Доступність: те, що ламається тихо
- Темінг за допомогою CSS-змінних: темний режим, бренд і перевизначення на сторінці
- Моделі доставки: вбудоване для кожного блоку vs SVG-спрайт vs символи
- CSP і безпека: як зберегти іконки без порушення політики
- Продуктивність і надійність: що уповільнює і чому
- План швидкої діагностики
- Практичні завдання (команди, виводи, рішення)
- Три міні-історії з корпоративного життя
- Поширені помилки: симптом → корінь → виправлення
- Чеклисти / покроковий план
- Поширені запитання
- Висновок: наступні кроки, які не засоромлять вас пізніше
Чому варто припинити використовувати шрифти-іконки
Шрифти-іконки були хитрим прийомом, коли ми боролися зі старими браузерами і не мали зручних інструментів для SVG. Вони досі «працюють», поки ви не додаєте реальні обмеження: політику безпеки вмісту, заходи приватності, перевірки цілісності ресурсів, особливості завантаження шрифтів і вимоги доступності, що не обмежуються фразою «воно відображається в мене».
Операційна правда
Якщо ваш інтерфейс залежить від файлу шрифту, щоб передати значення, ви створили залежність надійності, замасковану під типографіку.
Шрифти-іконки ламаються так, що виглядає як «випадкові баги UI»
FOIT/FOUT — це не лише проблема маркетингу. Коли шрифт не завантажується, іконки можуть відображатися як tofu (квадрати відсутніх гліфів), символи з приватної області юнікоду або неправильний гліф через вибір запасного шрифту. Це не лише некрасиво; це може вводити в оману. «Danger»-блок без іконки може бути прийнятним. Блок зі неправильною іконкою — це те, як хтось видаляє невірний набір даних.
Субсетинг шрифтів — це мінне поле. Хтось оптимізує вагу, вирізаючи шрифт до «тільки те, що ми використовуємо». Потім на новій сторінці з’являється іконка, якої немає в підмножині. У стажі все виглядає нормально (кешований старий повний шрифт), у продакшені — ні. Вітаємо, баг, який спалює день і кілька стосунків.
Доступність зі шрифтами-іконками — це переважно бажання. Екранні рідери не інтерпретують «гліф за кодовою точкою E018» як «попередження». Ви врешті-решт посипаєте прихований текст, і він розходиться з реальним відображенням.
CSP та корпоративні браузери: багато організацій блокують шрифти з сторонніх джерел, а деякі взагалі відключають віддалені шрифти. Іконний шрифт часто перший, що зникає. Вбудоване SVG, якщо зроблене правильно, може бути дружнім до CSP і самодостатнім.
Також: шрифти-іконки заохочують ліниве стилювання (просто встановіть font-size і все). SVG змушує розібратися з розмірами, viewBox і вирівнюванням. Це дратує десять хвилин, а потім рятує роками.
Жарт #1: Шрифти-іконки — це як RAID 0 з почуттів: шалено швидко, аж поки не треба, щоб було правильно.
Факти та трохи історії (корисно, не ностальгічно)
- Факт 1: Шрифти-іконки стали популярними, бо ранні інструменти та підтримка браузерів робили SVG незручним, тоді як шрифти вже кешувалися і добре стискалися.
- Факт 2: Багато шрифтів-іконок використовують Unicode Private Use Area, що явно робить значення та сумісність вашою проблемою.
- Факт 3: SVG 1.1 став рекомендацією W3C у 2011 році; масове впровадження затрималося через повільний розвиток екосистеми інструментів.
- Факт 4: Перехід від «зображень усюди» до «вбудованих SVG» прискорився, коли адаптивний дизайн зробив фіксовані растрові іконки болючими.
- Факт 5: CSS custom properties (змінні) з’явилися у сучасних браузерах близько 2017–2018 років, що спростило тематизацію компонентів без препроцесорів.
- Факт 6: Реальні CSP політики посилилися після гучних XSS інцидентів, які змусили компанії обмежити inline-скрипти, віддалені шрифти та небезпечні джерела.
- Факт 7: Поява prefers-color-scheme зробила шрифти-іконки менш зручними, бо часто хочеться, щоб лінії/заливки відповідали currentColor і токенам теми.
- Факт 8: «SVG-спрайти» (symbols) виникли як відповідь на повторювану розмітку вбудованих SVG, але вони внесли власні питання з крос-доменним доступом і кешуванням.
Цілі дизайну: що означає «хороші» блоки-сповіщення
Блоки-сповіщення — це не декор. Це елемент інтерфейсу для керування увагою. У продукційних системах і внутрішній документації вони часто є останнім запобіжником перед тим, як хтось зробить щось дороге. Отже, компонент має бути:
| Мета | Чому це важливо операційно | Як вбудоване SVG + CSS-змінні допомагають |
|---|---|---|
| Послідовність у різних середовищах | Корпоративні браузери, жорсткий CSP, заблоковані шрифти, офлайн-документація, експорт у PDF. | SVG самодостатній; темінг через змінні не залежить від відображення шрифтів. |
| Доступність за замовчуванням | Ризик аудиту, внутрішня відповідність, реальні люди, які користуються скрінрідерами. | Контроль ARIA, декоративні vs інформативні іконки, токени контрасту. |
| Можливість темінгу без перепису | Темний режим, рефреш бренду, вбудовування в портали партнерів. | CSS-змінні поширюються; ви перевизначаєте токени, а не селектори. |
| Продуктивність | Сайти з документацією і портали живуть або вмирають через сприйняту швидкість. | Жодного завантаження шрифту; іконки малюються з currentColor; уникнення зсувів макета. |
| Безпека під CSP | Команди з безпеки застосовують політики; винятки дорого обходяться політично. | Вбудоване SVG без скриптів; уникайте віддалених шрифтів і ризикового inline JS. |
| Підтримуваність | Ви забудете, навіщо це зроблено. Майбутній ви буде втомленим. | Розмітка явна; іконки — це path; токени централізовані. |
Орієнтована порада: ставтеся до блоків-сповіщень як до логування. Невелика структура спочатку запобігає великому хаосу пізніше. Потрібна невелика кількість типів (info/warn/danger/success), стабільний набір іконок і токени для accent/background/border. Все інше — дрібні дискусії.
Референсний компонент: HTML + вбудоване SVG + CSS-змінні
Це виробнича базова реалізація. Вона не вимагає кроку збірки. Не залежить від зовнішніх бібліотек іконок. Деградує розумно. І її дуже легко темізувати.
Шаблон розмітки (декоративна іконка)
Більшість іконок у блоках-сповіщеннях є декоративними. Тип блокування передається заголовком («Warning», «Note», «Danger»), а не лише іконкою. У цьому випадку ховайте SVG від допоміжних технологій за допомогою aria-hidden="true".
cr0x@server:~$ cat callout-example.html
<aside class="callout callout--warning" role="note">
<div class="callout__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
<path d="M10.3 4.3 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z"></path>
</svg>
</div>
<div>
<p class="callout__title">Warning</p>
<p class="callout__body">Do not run this migration twice. The second run will delete data you meant to keep.</p>
</div>
</aside>
Шаблон CSS (спочатку токени, потім селектори)
Зверніть увагу, як компонент використовує currentColor у SVG і визначає кольори через кастомні властивості. Це тримає темінг чистим: ви встановлюєте --accent, і іконка + рамка слідують за ним.
cr0x@server:~$ cat callout.css
.callout{
--accent: #60a5fa;
--c-bg: color-mix(in srgb, var(--accent) 16%, transparent);
--c-border: color-mix(in srgb, var(--accent) 35%, #223047);
display: grid;
grid-template-columns: 22px 1fr;
gap: 12px;
padding: 14px;
border-radius: 12px;
background: var(--c-bg);
border: 1px solid var(--c-border);
align-items: start;
}
.callout__icon svg{
width: 100%;
height: 100%;
display: block;
stroke: currentColor;
fill: none;
stroke-width: 1.9;
stroke-linecap: round;
stroke-linejoin: round;
}
.callout--warning{ --accent: #fbbf24; }
.callout--danger{ --accent: #fb7185; }
.callout--success{ --accent: #34d399; }
Інформація
Цей стиль використовує currentColor, тому іконка автоматично слідує за токеном accent.
Успіх
Та сама розмітка. Інші токени. Жодних додаткових класів іконок, жодних ваг шрифтів, без драми.
Коли не варто вбудовувати SVG
Вбудовувати SVG повсюди може роздути HTML, якщо на сторінці сотні іконок. Це реальний компроміс. Якщо на ваших сторінках документації десятки блоків-сповіщень, можливо, варто використовувати символ-спрайт або крок збірки для дедуплікації. Але не переходьте до спрайту просто тому, що це здається «правильним». Робіть це, коли вимірювана повторюваність справді впливає на критичний шлях.
Доступність: те, що ламається тихо
Більшість помилок доступності не виглядають як явні помилки. Вони виглядають як «працює у мене». Потім приходить аудит, або працівник подає скаргу, або команда продукту блокується від релізу. Блоки-сповіщення — прості компоненти, тому особливо незручно робити їх неправильно.
Декоративні vs інформативні іконки
Розберіться, чи іконка передає інформацію, яка відсутня в тексті. Якщо заголовок вже каже «Warning», то іконка декоративна. Використовуйте aria-hidden="true" для контейнера іконки або самого SVG і уникайте дублювання у озвучуванні.
Якщо іконка є єдиним індикатором (не рекомендовано), вона має мати доступну назву. Краще: завжди додавайте видимий заголовок.
Ролі та семантика
Використовуйте семантичні контейнери. <aside> добре підходить для блоків-сповіщень; він сигналізує «додатковий вміст». Для допоміжних технологій role="note" часто підходить. Уникайте зайвих ролей. Не перетворюйте кожен блок-сповіщення на alert; користувачі будуть ігнорувати їх, а скрінрідери оголосять їх агресивно.
Контраст кольору і темінг
Ваш accent — не те саме, що колір рамки. Рамка не те саме, що фон. Пов’язуйте їх змінними, але не припускайте, що одна кольорова величина працюватиме в усіх темах. Якщо треба обчислювати відтінки, використовуйте безпечні методи як color-mix() з розумними відсотками і перевіряйте контраст як у світлій, так і в темній схемах.
Режим невдачі доступності
Якщо ваш «warning»-блок покладається на жовтий на білому, це не попередження; це слабка підказка.
Поведінка фокуса та інтерактивний вміст у блоках-сповіщеннях
У блоках-сповіщеннях часто є посилання («Див. runbook»). Переконайтеся, що посилання мають видимі стилі фокусування, і уникайте розміщення інтерактивних контролів занадто близько до іконки, якщо це впливає на цілі торкання на мобільних. Сам контейнер блок-сповіщення не повинен бути фокусованим, якщо він не клікабельний. Клікабельні блоки зазвичай поганий UX: люди намагаються виділити текст і потрапляють на іншу сторінку.
Темінг за допомогою CSS-змінних: темний режим, бренд і перевизначення на сторінці
CSS-змінні — це нудний і найкращий інструмент. Вони конфігуруються під час виконання, природно наслідуються й не вимагають препроцесорів. Для блоків-сповіщень вам потрібен невеликий набір токенів, які можна перевизначати на різних рівнях:
- Глобальні значення (
:root) - Перевизначення тем (світла/темна або скіни продукту)
- Перевизначення на рівні контейнера (доксайт вбудований у портал партнера)
- Перевизначення на рівні компонента (певний блок-сповіщення потребує кастомного accent)
Стратегія токенів, що масштабується
Не встановлюйте сирі кольори для кожного типу у десятку файлів. Визначте семантичні токени для типів та обчислюйте похідні значення (фон, рамка) з передбачуваними правилами. Майбутній ви скаже вам подяку, коли доведеться робити ребрендинг без втрати якогось забутого файлу CSS.
cr0x@server:~$ cat tokens.css
:root{
--callout-radius: 12px;
--callout-pad: 14px;
--callout-gap: 12px;
--callout-info: #60a5fa;
--callout-warning: #fbbf24;
--callout-danger: #fb7185;
--callout-success: #34d399;
--callout-mix-bg: 16%;
--callout-mix-border: 35%;
}
.callout{
border-radius: var(--callout-radius);
padding: var(--callout-pad);
gap: var(--callout-gap);
--accent: var(--callout-info);
--c-bg: color-mix(in srgb, var(--accent) var(--callout-mix-bg), transparent);
--c-border: color-mix(in srgb, var(--accent) var(--callout-mix-border), #223047);
}
.callout--info{ --accent: var(--callout-info); }
.callout--warning{ --accent: var(--callout-warning); }
.callout--danger{ --accent: var(--callout-danger); }
.callout--success{ --accent: var(--callout-success); }
Темінг на рівні контейнера
Якщо ваша документація вбудована в інший додаток, не боріться з цим. Дозвольте хосту визначати accent, встановлюючи змінні на контейнері.
cr0x@server:~$ cat embed-example.html
<div class="partner-skin" style="--callout-info:#22c55e; --callout-warning:#a78bfa;">
<aside class="callout callout--info" role="note">...</aside>
<aside class="callout callout--warning" role="note">...</aside>
</div>
Цей inline style іноді контроверсійний. Якщо ви не можете його використовувати через CSP, застосуйте клас і визначте перевизначення в таблиці стилів. Принцип важливіший: перевизначайте токени на межі, а не окремі правила CSS.
Моделі доставки: вбудоване для кожного блоку vs SVG-спрайт vs символи
У вас є три реалістичні підходи:
- Вбудоване SVG для кожного екземпляра (просто, але багатослівно)
- Вбудований SVG-спрайт із <symbol> (дедуплікація, все ще локально)
- Зовнішній спрайт із посиланням через <use> (кешується, але є проблеми з крос-доменом)
Патерн A: вбудоване SVG для кожного блоку
Краще, коли на сторінці мало блоків-сповіщень і ви хочете максимальної надійності. Це також найпростіше відправити у статичному HTML, згенерованому з Markdown.
Патерн B: вбудований <symbol> спрайт у документі
Ви вставляєте прихований SVG на початку сторінки, визначаєте символи один раз і посилаєтесь на них через <use>. Це дедуплікує розмітку і залишається в межах того самого джерела документу, уникаючи зовнішніх запитів.
cr0x@server:~$ cat sprite-inline.html
<svg aria-hidden="true" style="position:absolute;width:0;height:0;overflow:hidden">
<symbol id="icon-warning" viewBox="0 0 24 24">
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
<path d="M10.3 4.3 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z"></path>
</symbol>
</svg>
<aside class="callout callout--warning">
<div class="callout__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<use href="#icon-warning"></use>
</svg>
</div>
<div>...</div>
</aside>
Слід остерігатися проблем браузерів із зовнішніми посиланнями та певними налаштуваннями CSP; вбудовані символи зазвичай найменш проблемні.
Патерн C: зовнішній спрайт
Корисно для великих сайтів, де важливе кешування між сторінками. Ризики з’являються, якщо ваше середовище блокує крос-доменний SVG, якщо CSP блокує його, або якщо ваш CDN переписує заголовки в несподіваний спосіб. Зовнішні спрайти також ускладнюють «експорт в одиночний HTML» (PDF, офлайн-пакети).
Моя порада: почніть з вбудованого для кожного блоку або вбудованих символів. Перейдіть на зовнішні спрайти лише після того, як виміряли реальну проблему з payload і перевірили сумісність із CSP у найжорсткішому середовищі.
CSP і безпека: як зберегти іконки без порушення політики
Команди з безпеки не зводять з вами проблем через забаву. Їх навчила практика. Вбудоване SVG зазвичай безпечне, якщо це лише path, без скриптів, обробників подій, зовнішніх посилань і без ін’єкції ненадійного вмісту.
Правила безпечного SVG (виробничий варіант)
- Використовуйте прості елементи:
<path>,<circle>,<rect>. Уникайте<foreignObject>. - Не включайте
<script>всередині SVG. Не смійтеся; таке трапляється. - Ніяких inline-обробників подій типу
onloadабоonclick. - Якщо SVG надходять від користувачів (CMS), робіть суворий серверний санітайзинг.
- Уникайте зовнішніх посилань у
<use>, якщо ви не контролюєте заголовки та CSP повністю.
«Надія — це не стратегія.»
— Генерал Г. Норман Шварцкопф
Ця цитата часто з’являється в інженерії, бо вона надто доречно застосовна: якщо ваша система іконок покладається на «має завантажитися», ви вбудували надію в інтерфейс.
Куди CSP вдаряє по блоках-сповіщень
Проблема рідко у самому SVG. Проблема — у рішеннях навколо нього:
- Inline-стилі заблоковано: якщо ви темізуєте через inline
style, вам може знадобитися CSPstyle-src 'unsafe-inline'або nonce/hash. Уникайте цього. Віддавайте перевагу класам і статичним CSS. - Зовнішні спрайти заблоковано: браузер може відмовити в завантаженні
<use href="...sprite.svg#id">залежно від політики, крос-доменних налаштувань і заголовків. - Санітайзери вирізають SVG: деякі HTML-санітайзери видаляють
<svg>повністю або прибирають атрибути, як-отviewBox. Ваш пайплайн може «дбати про безпеку», ламаючи рендеринг.
Продуктивність і надійність: що уповільнює і чому
Тут «продуктивність» — це не «SVG — швидкий». Це означає: сторінка швидко стає придатною, блоки-сповіщення не штовхають макет, і іконки не спричиняють дивних перерисовок. Вбудоване SVG допомагає, бо уникає завантаження шрифту й зменшує ймовірність пізньої заміни. Але можна й самостійно нашкодити.
Типові витрати на продуктивність
- Надмірний HTML: повторення шляху розміром 600 байт 60 разів стає реальними байтами. Інколи gzip вас рятує; інколи — ні.
- Нерегульований CSS: надто складні селектори на великому сайті можуть зробити перерахунок стилів дорожчим, ніж сам SVG.
- Зсув макета: якщо не резервувати місце під іконку, текст блок-сповіщення може стрибнути, коли SVG відмалюється або коли шрифти завантажаться.
- Вартість рендерингу: дуже деталізовані SVG (безліч точок, фільтри) можуть уповільнити малювання. Тримайте іконки простими.
Практична позиція щодо надійності
Для блоків-сповіщень надавайте перевагу іконкам на основі обведення зі стандартним viewBox (24×24 — поширено). Обмежте набір. Уникайте фільтрів. Не анімуйте. Іконка в блок-сповіщенні — не маркетинговий герой; це дорожній знак.
Жарт #2: Єдина припустима анімація в «warning»-блоці — ваше серцебиття, коли ви розумієте, що виконали команду в продакшені.
План швидкої діагностики
Коли іконки в блоках-сповіщень «ламаються», ви хочете швидко ідентифікувати вузьке місце: розмітка, CSS, CSP, санітайзер чи доставка? Ось порядок, що економить час.
По-перше: підтвердіть, що іконка є в DOM
- Відкрийте DevTools, інспектуйте блок-сповіщення.
- Чи є елемент
<svg>з атрибутомviewBox? - Чи присутні елементи
<path>?
Якщо SVG відсутній: ймовірно, його вирізав санітайзер, Markdown-рендерер або крок шаблонізації.
По-друге: перевірте обчислені стилі для розміру й кольору
- Підтвердіть, що контейнер іконки має явні width/height.
- Підтвердіть, що SVG використовує
stroke: currentColor(або fill) і щоcolorвстановлено на іконці/контейнері. - Перевірте, чи глобальний CSS reset не встановлює
svg { display: inline; }або не перекриває розміри.
Якщо SVG присутній, але невидимий: ймовірно, відсутній viewBox, або SVG використовує fill-шляхи, а ваш CSS змушує fill: none, або колір обчислюється як прозорий.
По-третє: виключіть CSP і зовнішні посилання
- Шукайте помилки в консолі про заблоковані ресурси.
- Якщо використовуєте зовнішні спрайти, протестуйте з вбудованим символ-спрайтом, щоб ізолювати крос-домен/CSP проблеми.
- Якщо темінг використовує inline-стилі, перевірте, чи CSP блокує inline-стилі.
По-четверте: виміряйте payload та повторюваність
- Перевірте розмір переданого HTML і CSS.
- Порахуйте блоки-сповіщень на сторінці і повторення однакових path-даних.
- Якщо їх багато: розгляньте символ-спрайт або дедуплікацію на етапі збірки.
Практичні завдання (команди, виводи, рішення)
Це реальні завдання, які ви можете виконати на Linux у CI чи на сервері, щоб діагностувати проблеми. Кожне завдання містить, на що звертати увагу та яке рішення прийняти.
Завдання 1: Перевірити, що зібраний HTML все ще містить SVG
cr0x@server:~$ rg -n "
Що означає вивід: Ви бачите теги SVG і атрибути viewBox у згенерованому виводі, а не лише в шаблонах джерела.
Рішення: Якщо цього немає, ваш Markdown-пайплайн або санітайзер вирізав SVG. Виправте конфіг рендерера, перш ніж чіпати CSS.
Завдання 2: Виявити відсутній viewBox (класична причина «невидимих іконок»)
cr0x@server:~$ rg -n "
Що означає вивід: Принаймні один тег SVG не має viewBox. Без нього масштабування непередбачуване і може схлопнутися до «нічого».
Рішення: Додайте viewBox до кожної іконки (встановіть стандарт 0 0 24 24). Не «виправляйте» це CSS-трансформаціями.
Завдання 3: Перевірити використання зовнішнього спрайту, що може викликати проблеми з CSP
cr0x@server:~$ rg -n "
Що означає вивід: Ви посилаєтесь на зовнішній спрайт через HTTP(S). Це може блокуватися CSP, крос-доменними обмеженнями або функціями приватності.
Рішення: Віддавайте перевагу вбудованим символам або спрайтам того ж походження, якщо ви не перевірили заголовки і CSP у найжорсткішому середовищі.
Завдання 4: Знайти inline-стилі темінгу, які можуть блокуватися CSP
cr0x@server:~$ rg -n "style=\"[^\"]*--callout" dist/**/*.html | head
dist/guide/embed.html:12:<div class="partner-skin" style="--callout-info:#22c55e">
Що означає вивід: CSS-змінні встановлюються через inline-стилі. Якщо CSP блокує inline-стилі, темінг мовчазно зламається.
Рішення: Перенесіть перевизначення токенів у класові стилі або налаштуйте CSP з nonces/hashes, якщо inline-style справді потрібен.
Завдання 5: Підтвердити, що CSS дійсно встановлює stroke: currentColor або fill: currentColor
cr0x@server:~$ rg -n "stroke:\s*currentColor|fill:\s*currentColor" dist/**/*.css
dist/assets/site.css:418:.callout__icon svg{stroke:currentColor;fill:none;stroke-width:1.9}
Що означає вивід: Іконки наслідують колір правильно, тому темінг через color або tokenи accent працюватиме.
Рішення: Якщо цього немає, вирішіть підхід: іконки з обведенням (stroke=currentColor, fill=none) або заповнені (fill=currentColor). Змішування обох підходів — шлях до «деякі іконки зникають у темному режимі».
Завдання 6: Виявити CSS reset-и, що ламають розміри SVG
cr0x@server:~$ rg -n "svg\s*\{|svg\s*,|svg\s+\w" dist/**/*.css | head
dist/assets/reset.css:33:svg{display:inline}
dist/assets/reset.css:34:svg{vertical-align:middle}
Що означає вивід: Глобальні reset-и впливають на SVG. Не завжди погано, але часто неперевірено.
Рішення: Переконайтеся, що ваш компонент явно встановлює width/height/display для SVG. Не покладайтеся на глобальні reset-и.
Завдання 7: Виміряти блоут HTML (повторювані вбудовані SVG)
cr0x@server:~$ wc -c dist/guide/backup.html
248312 dist/guide/backup.html
Що означає вивід: Ця сторінка ~248 КБ без стиснення. Може бути нормальним, може бути надмірним залежно від цілей сайту.
Рішення: Якщо це часто відбувається і сторінки великі з повторюваними іконками, розгляньте перехід на символ-спрайт для дедуплікації.
Завдання 8: Порахуйте блоки-сповіщень на сторінці (щоб передбачити повтори і ризик зсуву макета)
cr0x@server:~$ rg -c "class=\"callout\b" dist/guide/backup.html
27
Що означає вивід: 27 блоків-сповіщень на одній сторінці — це багато. Повторюваність важлива.
Рішення: Якщо ви в діапазоні 20–50, вбудовувати SVG для кожного екземпляра, ймовірно, неефективно. Перейдіть на символи або включення на етапі збірки.
Завдання 9: Підтвердити поведінку gzip/brotli для статичних ресурсів (бо байти важливі)
cr0x@server:~$ gzip -c dist/guide/backup.html | wc -c
42186
Що означає вивід: Gzip зменшує 248 КБ до ~42 КБ. Повторювані SVG-шляхи добре стискаються.
Рішення: Якщо стиснений розмір вже малий, не ускладнюйте архітектуру. Якщо він усе ще великий — переходьте на символи або зменшуйте складність іконок.
Завдання 10: Перевірити заголовки сервера на CSP, що можуть блокувати стилі або посилання на SVG
cr0x@server:~$ curl -sI https://docs.example.internal/guide/backup | rg -i "content-security-policy|x-content-type-options|x-frame-options"
content-security-policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
Що означає вивід: Inline-стилі заблоковані (style-src 'self' без 'unsafe-inline' або nonces). Data URL для зображень дозволено.
Рішення: Не використовуйте inline-style для темінгу; тримайте токени в CSS. Для зовнішніх спрайтів забезпечте same-origin ('self').
Завдання 11: Перевірити, що ваш санітайзер SVG не видаляє потрібні атрибути
cr0x@server:~$ node -p "require('fs').readFileSync('dist/guide/backup.html','utf8').includes('viewBox=')"
true
Що означає вивід: Принаймні один viewBox пережив пайплайн.
Рішення: Якщо false, виправте allowlist санітайзера (viewBox, aria-*, role) або припиніть подвійний санітайзинг довірених артефактів.
Завдання 12: Знайти випадкові іконки з fill=none, які мали бути заповненими
cr0x@server:~$ rg -n "fill=\"none\"" dist/**/*.html | head
dist/guide/alerts.html:77:<path fill="none" d="M12 2a10 10 0 1 0 0 20"></path>
Що означає вивід: Деякі шляхи явно задають заборону заливки. Якщо ваш набір іконок містить заповнені іконки, це може зробити їх невидимими.
Рішення: Уніфікуйте іконки: або видаліть атрибути fill і контролюйте через CSS, або забезпечте підтримку обох стилів за допомогою явних класів.
Завдання 13: Виявити дублювання path-даних, які слід винести в символ
cr0x@server:~$ rg -n "M10\.3 4\.3 2\.6 18" dist/**/*.html | wc -l
54
Що означає вивід: Такий самий warning-triangle path зустрічається 54 рази у виводі. Це дублювання.
Рішення: Якщо це зростає, переходьте на вбудований символ-спрайт або включення на етапі збірки для дедуплікації.
Завдання 14: Підтвердити, що перевизначення CSS-змінних дійсно застосовуються (швидкий grep для значень токенів)
cr0x@server:~$ rg -n "--callout-(info|warning|danger|success)" dist/**/*.css
dist/assets/site.css:12:--callout-info:#60a5fa;
dist/assets/site.css:13:--callout-warning:#fbbf24;
dist/assets/site.css:14:--callout-danger:#fb7185;
dist/assets/site.css:15:--callout-success:#34d399;
Що означає вивід: Токени присутні у деплойнутому CSS, а не лише в репозиторії.
Рішення: Якщо токени відсутні, ваш крок збірки видає файл або ваш бандлер tree-shake-ить змінні через неправильну конфігурацію.
Три міні-історії з корпоративного життя
Міні-історія 1: Інцидент через хибне припущення
Одна команда підтримувала внутрішню інструкцію з операцій, яка використовувалася під час інцидентів. У ній були блоки-сповіщення «Danger», «Warning» і «Note». Іконки відображалися через шрифт-іконку, розміщений на спільному домені активів. Припущення було простим: «Шрифт кешується скрізь, отже він фактично безкоштовний».
Потім відбулася зміна в безпеці. Корпоративний проксі почав видаляти відповіді з віддалених шрифтів, якщо вони не походили з allowlisted origin. Ніхто не координувався з командою документації, бо в великій компанії docs- сайт вважали «просто статичним контентом».
Під час реального інциденту респондери відкрили інструкцію з жорстким профілем браузера. Всі іконки-попередження перетворилися на порожні квадрати. Гірше: заголовки блоків були делікатні, а іконка виконувала більшість візуальної роботи. Один інженер пропустив примітку «не виконувати міграцію двічі» і повторно запустив скрипт. Це не знищило світ, але продовжило простій і створило невідповідність даних, яку потім довелося виправляти.
Післяінцидентна дискусія не була про шрифти. Вона була про припущення: сайт документації мав непомічену залежність від віддаленого шрифту, і ця залежність спрацювала в найневідповідніший момент.
Виправлення було грубим і правильним: вбудувати SVG для чотирьох типів блоків-сповіщень і доставляти їх разом зі сторінкою. Іконки стали частиною артефакту, а не мережею. Команда також посилила видимість заголовків, щоб іконка перестала бути основним сигналом.
Міні-історія 2: Оптимізація, що відпрацювала проти
Інша група побудувала витончену систему дизайну для внутрішніх інструментів. Вони замінили шрифти-іконок на один зовнішній SVG-спрайт, щоб зменшити дублювання HTML. Він розміщувався на CDN і використовувався через <use href="...icons.svg#id">. На діаграмах це виглядало елегантно.
Потім почали надходити звіти: «Іконки відсутні в Safari», «Іконки відсутні у PDF», «Іконки відсутні в вбудованих переглядах». Відсутність не була випадковою; вона корелювала з середовищами, що рендерили сторінки в обмежених контекстах. Деякі інструменти вбудовували інші інструменти через iframe з різними CSP-заголовками. Деякі користувачі експортували сторінки в PDF через сервіс, який не діставав крос-доменні ресурси. Деякі браузери по-різному поводилися з зовнішніми <use> посиланнями залежно від заголовків і кешування.
Команда намагалася виправити це більш тонким налаштуванням кешування і заголовків. Це допомагало в одному місці і ламало в іншому. Класика. Вони пожертвували кількома кілобаїтами дубльованої розмітки в обмін на розподілену проблему систем із походженням, заголовками і рендерами.
Поступовий компроміс був раціональний: вбудовані символ-спрайти для додатків, що потребують надійності (особливо інструменти для інцидентів), зовнішні спрайти лише для публічних маркетингових сторінок, де кешування важливе і CSP можна сформувати узгоджено. Система дизайну документувала обидва патерни і зробила надійний за замовчуванням.
Міні-історія 3: Нудна практика, що врятувала день
Команда платформ документації запускала статичний генератор з суворим HTML-санітайзером, бо контент писали багато команд. Підтримка SVG спочатку була відключена «для безпеки». Замість того, щоб обхідним шляхом вимикати санітайзер, вони зробили нудну річ: створили маленький allowlist для обмеженого піднабору SVG, що використовувалися лише для іконок.
Allowlist дозволив <svg> з viewBox, focusable і ARIA-атрибутами; дозволив <path> з атрибутом d; і видалив усе інше. Жодних скриптів. Жодного foreignObject. Жодних зовнішніх посилань. Вони також зафіксували стандартний розмір іконки і прибрали inline-стилі.
Місяць потому інша команда випадково вставила складний SVG, експортований з інструменту дизайну, у блок-сповіщення. Санітайзер обрізав його до нічого корисного, а білд не пройшов через lint-правило, що виявляло «порожню іконку». Автор отримав чітке повідомлення про помилку з інструкціями щодо виправлення.
Це не було гламурно. Але це запобігло класу проблем з безпекою та продуктивністю і зробило «SVG іконки в блоках-сповіщень» безпечною за замовчуванням практикою, а не винятком, за який треба просити дозвіл у відділу безпеки.
Поширені помилки: симптом → корінь → виправлення
1) Симптом: іконки відображаються як порожні квадрати або випадкові літери
Корінь: Десь іще використовується шрифт-іконка, і шрифт не завантажився або був замінений.
Виправлення: Приберіть шрифти-іконки для блоків-сповіщень. Замініть їх на вбудовані SVG. Також перевірте CSS на кшталт .icon:before { content: "\e018"; font-family: ... }, що може лишатися в кодовій базі.
2) Симптом: SVG у DOM, але нічого не видно
Корінь: Відсутній viewBox, або SVG використовує fill-шляхи, а ваш CSS примушує fill: none, або колір обчислюється як прозорий.
Виправлення: Уніфікуйте всі іконки з viewBox; визначте, stroke чи fill; встановіть stroke: currentColor або fill: currentColor відповідно; перевірте обчислений color на контейнері іконки.
3) Симптом: іконки видно на деяких сторінках, але відсутні на інших
Корінь: Зовнішні спрайти блокуються CSP або крос-доменними умовами, або шляхи збирання відрізняються між секціями.
Виправлення: Використовуйте вбудовані символи для документації та внутрішніх інструментів. Якщо потрібно зовнішні спрайти, тримайте їх same-origin і перевіряйте CSP та заголовки у всіх контекстах (включаючи iframe і сервіси генерації PDF).
4) Симптом: макет блок-сповіщення стрибає при завантаженні сторінки
Корінь: Простір під іконку не зарезервований (немає width/height на контейнері), або шрифти завантажуються пізно і змінюють висоту рядків.
Виправлення: Задайте фіксований розмір контейнеру іконки та встановіть SVG як display: block. Тримайте типографіку стабільною і уникайте замін шрифтів для критичних елементів UI.
5) Симптом: темний режим робить рамки брудними або з низьким контрастом
Корінь: Похідні кольори налаштовані лише для світлої теми; color-mix утворює «брудні» рамки на темному фоні.
Виправлення: Визначайте окремі відсотки для light/dark або явні токени рамки для кожної теми. Перевіряйте контраст, не орієнтуйтесь лише на око.
6) Симптом: скрінрідери повторно оголошують «graphic» або читають заголовок іконки
Корінь: SVG не позначено як декоративне, або ви випадково використали <title> у SVG без наміру.
Виправлення: Для декоративних іконок використовуйте aria-hidden="true". Для значущих іконок — давайте коректну мітку і не дублюйте заголовок блок-сповіщення.
7) Симптом: деякі іконки вертикально зміщені відносно тексту
Корінь: SVG — inline-level і вирівнюється по базовій лінії; співвідношення сторін viewBox відрізняються між іконками.
Виправлення: Використовуйте display: block для SVG, фіксований контейнер і уніфікуйте viewBox та візуальний центр іконок.
8) Симптом: іконки зникають після випуску «закріплення безпеки»
Корінь: Санітайзер вирізає елементи/атрибути SVG, або CSP блокує потрібні inline-стилі для темінгу.
Виправлення: Створіть явний allowlist SVG для іконок; видаліть inline-стилі темінгу; переконайтеся, що всі потрібні атрибути (viewBox, aria-hidden) дозволені.
Чеклисти / покроковий план
Крок за кроком: міграція від шрифтів-іконок до вбудованих SVG-блоків
- Інвентаризація поточних блоків-сповіщень. Перерахуйте типи і де вони з’являються (документація, UI продукту, електронні листи, експорт).
- Визначте мінімальний набір іконок. Info, warning, danger, success зазвичай достатньо. Менше іконок — менше розбіжностей.
- Уніфікуйте геометрію іконок. Виберіть стандартний viewBox (зазвичай 0 0 24 24) і однакову товщину лінії.
- Виберіть стиль: stroke чи fill. Змішані набори можливі, але потрібен додатковий CSS. Тримайте все простим, якщо дизайн не вимагає іншого.
- Побудуйте компонент із токенами. Використовуйте
--accent, обчислюйте--c-bgі--c-border. - За замовчуванням робіть іконки декоративними. Вимагайте видимих заголовків. Ховайте SVG з
aria-hidden, якщо немає особливої причини інакше. - Тестуйте під жорстким CSP. Особливо перевіряйте при заблокованих inline-стилях і віддалених шрифтах.
- Тестуйте темний режим і режими високого контрасту. Перевірте рамки й контраст тексту.
- Запускайте HTML/SVG lint у CI. Виявляйте відсутні viewBox, порожні шляхи або вирізані атрибути.
- Вимірюйте payload і повторюваність. Якщо блоут справжній, перейдіть від вбудовуваних екземплярів до вбудованих символів.
- Документуйте патерн. Не роман: один приклад, список do/don’t і токени для перевизначення.
- Видаліть залежності від старих шрифтів-іконок. Не залишайте привидів CSS. Привиди CSS завжди повертаються, щоб вас преслідувати.
Чеклист: готовність до релізу для іконок у блоках-сповіщень
- Усі SVG іконок у блоках-сповіщень містять
viewBoxі рендеряться при 1x і 2x без обрізань. - Іконки наслідують колір через
currentColor(stroke або fill) і видимі в темному режимі. - Заголовки блок-сповіщень видимі і передають тип без залежності від іконки.
- Декоративні іконки мають
aria-hidden="true". - Немає зовнішніх залежностей від шрифтів для іконок.
- Сторінки правильно рендеряться з CSP: немає потреби в inline-стилях, немає заблокованих ресурсів.
- Продуктивність: іконки не викликають зсувів макета; розміри контейнера фіксовані.
Поширені запитання
1) Чому вбудоване SVG замість бібліотеки іконок?
Бібліотеки підходять, якщо ви контролюєте ланцюжок збірки і можете гарантувати консистентний вивід. Для блоків-сповіщень ви хочете мінімальну та передбачувану поверхню залежностей. Вбудоване SVG — явне й портативне рішення, особливо для статичної документації та інструкцій для інцидентів.
2) Чи варто використовувати заповнені чи штрихові іконки?
Виберіть один стиль для всієї системи блоків-сповіщень. Штрихові іконки з stroke: currentColor легко темізувати і виглядають добре в малих розмірах. Заповнені іконки теж підходять, але контролюйте fill послідовно і уникайте змішування семантик.
3) Чи безпечно використовувати color-mix()?
Це широко підтримується в сучасних браузерах, але якщо у вас є вимоги щодо застарілих браузерів, можливо, доведеться використовувати явні токени (попередньо обчислені кольори для фону/рамки). Операційно: якщо ви не контролюєте базу браузерів, не обчислюйте кольори під час виконання.
4) Як уникнути надмірного розміру HTML, якщо вбудовувати SVG повсюди?
Використовуйте вбудований символ-спрайт (<symbol>) на початку документу і посилайтеся через <use>. Це дедуплікує без зовнішніх запитів. Якщо пайплайн підтримує, можна також включати іконки як partials на етапі збірки.
5) Чи порушують вбудовані SVG CSP?
Вбудоване SVG — це не inline-скрипт. Проблеми виникають через скрипти всередині SVG, обробники подій або зовнішні посилання. Тримайте SVG простими і санітизованими, якщо контент ненадійний. Також уникайте inline-атрибутів style для темінгу, якщо CSP їх блокує.
6) Який найкращий семантичний HTML-елемент для блок-сповіщення?
<aside> — хороший дефолт. Можна додати role="note", якщо це допомагає допоміжним технологіям. Уникайте role="alert", якщо блок не містить термінової динамічної інформації, що вимагає негайного оголошення.
7) Мій санітайзер видаляє viewBox. Чи можу я обійтися без нього?
Можна, але не слід. Без viewBox масштабування крихке і може призвести до порожнього рендерингу. Виправляйте allowlist санітайзера. Якщо платформа не може безпечно дозволити viewBox, потрібна інша стратегія рендерингу (наприклад, серверний рендеринг у безпечну розмітку), а не CSS-лайфхаки.
8) Як забезпечити ідеальне вирівнювання іконки та заголовка незалежно від шрифтів?
Використовуйте фіксований контейнер для іконки і встановіть SVG як display: block. Коригуйте margin-top лише за потреби, але намагайтеся вирівнювати за допомогою однакового line-height і розміру коробки іконки. Не покладайтеся на вирівнювання по базовій лінії для SVG.
9) Чи можна вбудувати SVG як data URL?
Можна, але зазвичай це гірше для підтримки і може блокуватися CSP (правила img-src). Вбудоване SVG ясніше, легше темізувати і перевіряти.
10) Що робити, якщо моя документація написана в Markdown і рендерер видаляє HTML?
Тоді потрібне розширення Markdown для блоків-сповіщень, що рендерить довірений HTML на етапі збірки, або система шорткодів/компонентів. Не просіть авторів боротися з рендерером; зробіть «правильний спосіб» простим і послідовним.
Висновок: наступні кроки, які не засоромлять вас пізніше
Блоки-сповіщення — дрібні компоненти, але вони живуть на перетині надійності і людської уваги. Вбудоване SVG + CSS-змінні — практичний вибір, бо зменшує залежності, переживає жорсткі середовища і тримає темінг у межах здорового глузду.
Наступні кроки:
- Уніфікуйте чотири типи блоків-сповіщень і маленький набір іконок зі спільним viewBox та товщиною лінії.
- Реалізуйте компонент на базі токенів (accent/background/border) і ховайте декоративні іконки від допоміжних технологій.
- Запустіть діагностичні завдання вище в CI: перевірте відсутні viewBox, випадкові зовнішні посилання на спрайти та залишки шрифтів-іконок у CSS.
- Протестуйте під найжорсткішою CSP і в темному режимі перед релізом.
- Якщо виникне бум навантаження payload, перейдіть на вбудовані символи — не стрибайте одразу на зовнішні спрайти, якщо не контролюєте заголовки повсюди.
Якщо запам’ятаєте лише одне
Зробіть сенс видимим у тексті; нехай іконка підсилює його. Потім зробіть іконку самодостатньою, темізованою і нудною.