Виклики в документації, які вас не підведуть: CSS-змінні, темний режим і операційна дисципліна

Було корисно?

Ви випускаєте редизайн документації в п’ятницю. У понеділок чергові бачать блок «НЕБЕЗПЕКА», який у темному режимі виглядає як дружня ПОРАДА, і хтось вставляє продакшн-токен не туди. Це не «просто CSS». Це інцидент операцій з приємним шрифтом.

Блоки сповіщень (ЗАМІТКА/ПОРАДА/ПОПЕРЕДЖЕННЯ/НЕБЕЗПЕКА) — це невеликі UI-компоненти з непропорційно великою відповідальністю. Вони ущільнюють ризик у кольорі, бордері й кількох словах. Якщо тема зроблена недбало, ви привчаєте користувачів ігнорувати ці блоки — а потім дивуєтесь, чому ніхто не виконав рунабук.

Що насправді роблять виклики (і чому це має хвилювати операції)

У документації адмоніції — це UI-еквівалент огороджень. Вони не декоративні; вони — орган управління. ЗАМІТКА каже «не дивуйся». ПОРАДА каже «зроби це, щоб швидше». ПОПЕРЕДЖЕННЯ каже «тут можна нашкодити собі». НЕБЕЗПЕКА каже «тут можна нашкодити всім іншим».

Таке намірення. Реальність гірша: більшість викликів реалізовано як розширення Markdown, що мапиться на CSS-клас, а потім темізується тим, що було в дизайн-системі того тижня. У світлому режимі вони виглядають нормально; у темному режимі вони перетворюються на низькоконтрастну кашу або, ще гірше, ієрархія серйозності інвертується. Користувачі перестають довіряти візуальним підказкам і пролистують текст. Коли інтерфейсу не довіряють, оператори роблять те, що вони завжди роблять у втомі: припускають.

ПОПЕРЕДЖЕННЯ

Якщо ваші стани ПОПЕРЕДЖЕННЯ та НЕБЕЗПЕКА не чітко відрізняються в темному режимі, це помилка надійності документації. Ставтеся до неї як до дефекту.

CSS-змінні (custom properties) — найкращий інструмент для забезпечення консистентності викликів між фреймворками, сторінками й режимами. Не тому, що це модно, а тому, що вони дають:

  • Одне місце для визначення значення (токени), багато місць для використання (компоненти).
  • Перемикання на рівні виконання (темний режим, висококонтрастний режим) без перезбирання CSS.
  • Безпечні запасні значення, коли токен відсутній або перейменований якимось «корисним» пакетом теми.

Тут присутній менталітет надійності: UI документації має деградувати безпечно. Якщо змінна відсутня, ви хочете нудне значення за замовчуванням, а не невидимий бордер і сіре на сірому текст.

ЗАМІТКА

«Документація статична». Так. Як і повідомлення про помилки в масиві зберігання — аж до моменту, коли вони спричиняють аутедж.

Одна перефразована ідея, яку варто пам’ятати: «Все ламається; проектуйте так, щоб воно лама́лося безпечно.» — приписують мисленню надійності John Allspaw

Жарт №1: Темний режим як RAID — всі його хочуть, і хтось усе одно налаштує неправильно.

Факти та історія, які знадобляться на нарадах

Це не тривія заради тривії. Це аргументи для розмов «чому ми витрачаємо час на темування викликів?»

  1. Розмітка «адмоніцій» існувала до більшості сучасних інструментів для документації. Sphinx популяризував директиви на кшталт .. note:: задовго до того, як нинішні стеки на базі Markdown їх нормалізували.
  2. GitHub-flavored Markdown не стандартизував адмоніції. Багато екосистем реалізували власний синтаксис, тому портативність досі — плутанина.
  3. CSS custom properties широко з’явилися в браузерах приблизно з 2017 року. Це важливо, бо аргумент «ми не можемо покладатися на змінні» зазвичай — застаріла пам’ять організації.
  4. prefers-color-scheme став практичним лише після впровадження системного темного режиму. Раніше багато сайтів використовували ручні перемикачі й куки-хакі для збереження вибору.
  5. Ранні теми інвертували кольори. Інвертування ламає семантичні кольори: жовті попередження стають нечитаємими, сині — неоновими, а сірі — дивними. Сучасна темізація коригує люмінанс, а не лише відтінок.
  6. Рекомендації WCAG стосуються читабельності тексту, а не брендового вигляду. «На моєму MacBook виглядає нормально» — не стратегія контрасту.
  7. Кольори викликів мають культурні коннотації. Червоне сигналізує небезпеку в багатьох місцях, але не скрізь. Тому значення іконок та міток важливі.
  8. Дизайн-токени з’явилися в великих UI-системах для кросплатформної консистентності. Мета полягала в узгодженості між web/iOS/Android, а не лише в «чистішому CSS».

Дизайн-токени з CSS-змінними: розумний підхід

Припиніть думати про виклики як «синя коробка» або «жовта коробка». Думайте про токени: акцент, фон, бордер і, іноді, колір іконки. Токени репрезентують значення, а не реалізацію.

Ієрархія токенів, яка вам справді потрібна

Надійна ієрархія попереджає «війни перекриттів»:

  • Глобальні базові токени: --bg, --fg, --border.
  • Семантичні токени: --note-accent, --warn-bg тощо.
  • Токени компонентів: --callout-accent, --callout-border, які задає клас компонента.

Це важливо, бо компоненти повинні споживати семантичні токени, а не сирі hex-значення. Коли ви змінюєте колір попередження один раз, весь сайт має наслідувати. CSS вище робить саме це, маплячи .callout.warn на --warn-*.

ПОРАДА

Називайте токени буденно. Якщо ви назвете токен --sunset-glow-500, рано чи пізно ви відправите «сонячний» блок НЕБЕЗПЕКИ. Користувачі будуть у захваті, аж поки не видалять неправильний кластер.

Запасні значення: що робити, коли токен відсутній?

У продуктивних системах ви плануєте відсутність залежностей. Те саме тут. Використовуйте fallback-значення агресивно:

  • border: 1px solid var(--callout-border, var(--border)); означає «якщо відсутній бордер для виклику, використай глобальний бордер».
  • Ніколи не покладайтеся лише на background для передачі значення. Бордери й заголовки переживуть більше колізій тем.
  • Зробіть текст мітки («ПОПЕРЕДЖЕННЯ», «НЕБЕЗПЕКА») першим класом UI-елемента, а не псевдоелементом, який губиться при заміні шрифту.

Де зберігати токени

Розміщуйте токени якомога ближче до кореня документа (:root). Якщо ваш фреймворк обмежує стилі в скоупи, переконайтеся, що токени лишаються глобально доступними або навмисно обмежені для певної області документації. Змішування моделей скоупінгу — шлях до того, що виклики рендеряться по-різному в сайдбарі й у основному вмісті.

Рівень серйозності Семантичні токени Споживання компонентом Операційна мета
ЗАМІТКА --note-accent, --note-bg, --note-border .callout.note задає --callout-* Інформувати, а не відволікати
ПОРАДА --tip-accent, --tip-bg, --tip-border Такий самий патерн Заохотити кращу практику
ПОПЕРЕДЖЕННЯ --warn-accent, --warn-bg, --warn-border Такий самий патерн Запобігти помилкам
НЕБЕЗПЕКА --danger-accent, --danger-bg, --danger-border Такий самий патерн Зупинити небезпечні дії

Темний режим без візуальних оман

Темізування — це не «інвертуй і сподівайся». Ваші очі адаптуються інакше при низькому освітленні, а кольори поводяться по-іншому на майже чорному фоні. Основний режим відмови: ви зберігаєте ті ж акценти, але опускаєте фон до низької непрозорості, і раптом читається лише мітка. Користувачі інтерпретують весь блок як «малоприпустимий», бо він візуально відступає.

Використовуйте color-scheme і дайте платформі допомогти

Встановіть color-scheme: light dark в корені. Це сигналізує форм-контролам і скроллбарам узгоджуватися. Це також зменшує шанс, що нативний контрол всередині виклику виглядатиме так, ніби він телепортований з 2009 року.

Оберіть стратегію: затемнюйте фони, потім коригуйте бордери й текст

У темному режимі фони мають загалом бути підфарбованими (низька альфа), а бордери — сильнішими, ніж здається. Бордери визначають форму, коли все інше темне. Тому приклад CSS використовує:

  • --*-bg: rgba(...,.10) для м’яких заповнень
  • --*-border: rgba(...,.35) для визначення форми й підказки про серйозність
НЕБЕЗПЕКА

Якщо ваша темна НЕБЕЗПЕКА виглядає спокійніше за світлу ЗАМІТКУ, ієрархія серйозності інвертована. Користувачі дотримуватимуться тієї ієрархії, яку ви відтворюєте, а не тієї, яку ви задумали.

Не довіряйте монітору. Інструментуйте тему.

Так, потрібно робити візуальний QA. Але також слід трактувати CSS-змінні як конфігурацію і перевіряти їх у CI. Якщо пакет тем оновився й мовчки змінив --warn-accent, ви хочете диф, який ламає збірку, а не скаргу клієнта «попереджувальні блоки стали… спокійні».

Жарт №2: Єдина річ темніша за темний режим — душа людини, яка змерджила «швидку правку контрасту» прямо в main.

Патерни реалізації для адмоніцій

Ваш генератор документації може бути Markdown, MDX, AsciiDoc, reStructuredText або щось внутрішньо засране. Нижченаведені патерни переживуть зміну інструментів.

Патерн A: семантичний HTML + класи

Рендерьте адмоніції як реальний елемент з заголовком. Уникайте псевдоелементів для критичного тексту; вони крихкі для інструментів доступності та перекладу.

cr0x@server:~$ cat ./callout-example.html
<div class="callout warn" role="note" aria-label="Warning callout">
  <div class="icon" aria-hidden="true"></div>
  <div>
    <div class="title">WARNING</div>
    <p>Do not run this command against production.</p>
  </div>
</div>

Рішення: якщо ваш конвеєр не може виводити семантичний HTML як цей, виправте конвеєр. Стилізація — легка частина; надійна структура — важка.

Патерн B: data-атрибути для серйозності

Коли документація генерується з контенту, data-атрибути можуть зменшити кількість класів:

  • <aside class="callout" data-kind="warn">
  • CSS: .callout[data-kind="warn"] { ... }

Це також полегшує запити в тестах.

Патерн C: один компонент виклику, багато тем

Якщо у вас кілька продуктів або «просторів», не розмножуйте CSS для викликів. Зберігайте один компонент. Переопрацьовуйте токени для кожного простору.

Приклад підходу:

  • :root[data-space="storage"] { --note-accent: ... }
  • :root[data-space="compute"] { --note-accent: ... }

Операційно це допомагає уникнути спіралі тикетів підтримки «ПОПЕРЕДЖЕННЯ виглядає по-різному в продукті A».

Патерн D: forced-color і підтримка високого контрасту

Режим forced-colors у Windows ігноруватиме вашу гарну палітру. Це добре. Ви хочете, щоб ОС перемагала. Додайте мінімальні правила:

  • @media (forced-colors: active) { .callout { border: 1px solid CanvasText; } }
  • Переконайтеся, що заголовки залишаються видимими без залежності від фону.

Доступність: контраст, семантика та «не покладайтесь лише на колір»

Доступність — не податок на відповідність; це спосіб зберегти значення за ворожих умов: дешеві монітори, яскраве сонце, режим при мігрені або людина, яка читає о 03:00 на затемленому ноутбуку під час інциденту.

Контраст: тестуйте текст, не настрій

Для блоків сповіщень зосередьтеся на:

  • Контраст заголовка щодо фону виклику.
  • Контраст основного тексту щодо фону виклику.
  • Видимість бордера щодо фонового кольору сторінки.

Типова пастка: ви проходите контраст на заголовку, але програєте на посиланнях всередині виклику, бо колір посилань глобальний. Ось чому токени мають враховувати контраст посилань теж (або принаймні уникати надто блідих тонувань фону, що роблять посилання невидимими).

Семантика: зробіть мітку та серйозність машиночитними

Використовуйте видиму мітку і подумайте над aria-label. Екранний рідер не повинен вгадувати, що бордер жовтий. Люди теж не повинні.

Не кодуйте серйозність лише кольором

Використовуйте принаймні два сигнали:

  • Текст мітки: ЗАМІТКА/ПОРАДА/ПОПЕРЕДЖЕННЯ/НЕБЕЗПЕКА.
  • Форма іконки (навіть проста).
  • Товщина або патерн бордера (тонко, але корисно).
ЗАМІТКА

Якщо ваші виклики відрізняються лише відтінком, ви будуєте UI, який провалиться при першій спробі друку або на монохромному e-ink дисплеї.

Три корпоративні міні-історії з полів темізації

Міні-історія №1: інцидент через хибне припущення

Середня компанія мігрувала внутрішню документацію на новий статичний генератор сайтів. Власники контенту були задоволені: швидші збірки, привабливіші сторінки, перемикач теми. Техлід погодив, бо «це документація, а не продакшн».

Міграція включала адмоніції. Стара система рендерила ПОПЕРЕДЖЕННЯ й НЕБЕЗПЕКУ з різними іконками та дуже відмінними бордерами. Нова система використала ту саму іконку для обох, а автор теми припустив, що червоне тонування фону буде «очевидно небезпечним». У світлому режимі це в основному спрацьовувало. У темному режимі фони були низькоопаковими, і фон сторінки майже чорний, що поглинув різницю.

Через місяць хтось виконав рунабук для ротації облікових даних для спільної інтеграції. Там була НЕБЕЗПЕКА: «Цей крок інвалідизує токени для всіх середовищ; координуйте». У темному режимі це виглядало як м’яке попередження, людина припустила, що вплив локальний, і діяла самостійно.

Результат: декілька сервісів втратили авторизацію за лічені хвилини. Інцидент не спричинила неправильна аутентифікація; його спричинило хибне припущення про те, як UI передає ризик. Післяінцидентне вирішення було не «навчити інженерів читати краще». Воно полягало в: примусовій диференціації серйозності через токени, додаванні автоматичних перевірок контрасту і вимозі унікальних іконок для ПОПЕРЕДЖЕННЯ проти НЕБЕЗПЕКИ.

Міні-історія №2: оптимізація, яка дала відкат

Інша організація вирішила, що їхній CSS-бандл занадто великий. Хтось запропонував «оптимізувати», видаливши семантичні токени і дозволивши збірці вбудовувати hex-кольори прямо в CSS компонентів. Менше змінних, менше байтів, трохи швидше завантаження сторінки. Дашборд продуктивності трохи покращився, і так ці ідеї отримують поштовх.

Потім сталася ребрендингова зміна. Дизайн-система оновила кольори акцентів. Більшість компонентів підхопили нову палітру, бо були токен-орієнтовані. Але адмоніції тепер були захардкоджені в зкомпільованому CSS. Половина сторінок мала старі кольори, половина — нові. У темному режимі нова палітра працювала, стара — ні. Користувачі почали скаржитися, що ПОПЕРЕДЖЕННЯ нечитаемо — лише в певних розділах документації.

Команда витратила дні на погоню за «проблемами кешування» і «поширенням CDN». Реальність була більш бентежною: оптимізація знищила єдине джерело істини. Виправлення — відновити CSS-змінні, прийняти трохи більший CSS-файл і встановити реальні бюджети: вимірювати розмір бандла, але не випорожнювати підтримуваність заради байтів.

Вони також засвоїли важкий урок: менший CSS-файл не обов’язково «швидший», якщо це викликає більше витрат людино-годин і помилок у продакшні. Затримка — це не лише мілісекунди.

Міні-історія №3: нудна, але правильна практика, яка врятувала день

Велика команда підприємства підтримувала документацію для платформи зберігання з великою кількістю команд «ти можеш видалити все». Їхню документацію трактували як частину продукту, тож зміни проходили через CI з тестами. Не ефектно. Дуже ефективно.

У них було правило: токени для викликів живуть в одному файлі, і кожна зміна цього файлу тригерить візуальний регресійний прогін плюс простий тест «наявності токенів». Вони також тримали маленьку сторінку «severity ladder», яка рендера всі чотири виклики з посиланнями й блоками коду в обох режимах.

Одного дня оновлення залежності змінило базовий токен фону. Це не було зловмисно; це був рефакторинг. Виклики все ще рендерилися, але контраст бордера ПОПЕРЕДЖЕННЯ впав, а фон НЕБЕЗПЕКИ став надто тонким. Візуальний регресійний прогін зловив це до мерджу.

Жодної драми, жодних нічних відкатів. Виправлення — невелика корекція альфи бордера для темного режиму. Ось що купує «нудна правильність»: ви ловите семантичні регресії рано, і ваша документація не навчає людей робити неправильні речі.

Практичні завдання: команди, результати та рішення

Проблеми темізації документації найкраще відлагоджувати як будь-яку іншу продуктивну проблему: відтворити, виміряти, ізолювати, виправити, перевірити і запобігти регресіям. Ці завдання припускають типовий статичний репозиторій документації з директорією збірки (наприклад build/ або dist/), CSS-ресурсами та локальним dev-сервером.

Завдання 1: Знайдіть, де адмоніції рендеряться у згенерованому HTML

cr0x@server:~$ rg -n "callout|admon|alert|data-kind" build/ -S | head
build/ops/runbook/index.html:214:<div class="callout warn" role="note" aria-label="Warning callout">
build/ops/security/index.html:88:<aside class="callout" data-kind="danger">
build/styles/main.css:1023:.callout.warn{--callout-accent:var(--warn-accent);}

Що це означає: Тепер ви знаєте фактичну форму DOM і селектори, які використовуються в продакшн-видачі.

Рішення: Якщо вивід відрізняється по сторінках (div vs aside), уніфікуйте його. Непостійна розмітка гарантує непостійний стиль і тести.

Завдання 2: Знайдіть визначення токенів і переконайтеся, що вони не дублюються

cr0x@server:~$ rg -n --hidden --glob '!**/node_modules/**' ":root|--note-accent|--warn-accent|--danger-accent" .
./src/styles/tokens.css:1::root{
./src/styles/tokens.css:22:  --note-accent: #2563eb;
./src/styles/tokens.css:30:  --warn-accent: #b45309;
./src/styles/tokens.css:38:  --danger-accent: #dc2626;
./src/styles/theme-overrides.css:11::root{ --warn-accent: #a16207; }

Що це означає: У вас є множинні визначення; останнє перемагає під час виконання.

Рішення: Консолідуйте токени або документуйте пріоритети перекриттів. Якщо ви не можете пояснити пріоритет одним реченням, ви ним не керуєте.

Завдання 3: Перевірте, що темний режим керується prefers-color-scheme, а не крихким класом

cr0x@server:~$ rg -n "prefers-color-scheme|data-theme=|class=.*dark" src/styles -S
src/styles/tokens.css:45:@media (prefers-color-scheme: dark){
src/styles/app.css:12:html[data-theme="dark"] .sidebar{ background: #0b1020; }

Що це означає: Ви змішуєте автоматичний і ручний темний режим.

Рішення: Виберіть одне джерело істини. Якщо ви підтримуєте ручний перемикач, він має навмисно переважати prefers-color-scheme (і це має бути послідовно реалізовано).

Завдання 4: Запустіть локальний сервер і відтворіть проблему в обох режимах

cr0x@server:~$ npm run dev
> docs@1.0.0 dev
> vite

  VITE v5.4.0  ready in 430 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: http://192.168.1.20:5173/

Що це означає: У вас є надійне середовище відтворення.

Рішення: Якщо не можете відтворити локально, не «правіть» продакшн-CSS. Спершу уніфікуйте середовища збірки (версія Node, lockfile, CI).

Завдання 5: Підтвердіть фінальні обчислені змінні в збудованому CSS

cr0x@server:~$ rg -n --no-heading --fixed-strings "--warn-bg" build/assets/*.css | head -n 5
build/assets/main-9a8c1.css:1:...--warn-bg:#fffbeb;--warn-border:#fde68a;...
build/assets/main-9a8c1.css:1:...@media (prefers-color-scheme:dark){:root{--warn-bg:rgba(251,191,36,.1);--warn-border:rgba(251,191,36,.35)}}

Що це означає: Токени присутні у вихідних файлах і мають значення для світлого і темного режимів.

Рішення: Якщо темні перевизначення відсутні, ваш збірник може викидати медіа-блоки або скоупити їх. Виправте конфігурацію бандлера перед тим, як правити кольори.

Завдання 6: Перевірте, чи селектор виклику не перекривається пізніше в порядку CSS

cr0x@server:~$ rg -n "\.callout\.warn|\[data-kind=\"warn\"\]" build/assets/*.css | head -n 20
build/assets/main-9a8c1.css:1023:.callout.warn{--callout-accent:var(--warn-accent);--callout-bg:var(--warn-bg);--callout-border:var(--warn-border)}
build/assets/vendor-a12ff.css:887:.callout{border:1px solid transparent;background:transparent}

Що це означає: CSS від вендора може скидати ваш компонент після ваших правил, залежно від порядку завантаження.

Рішення: Виправте порядок завантаження або обережно підвищте специфічність. Не починайте війну специфічностей; ви програєте і заслужите це.

Завдання 7: Перевірка на відсутні токени за допомогою швидкого списку grep

cr0x@server:~$ rg -o --no-filename "var\\(--[a-z0-9-]+\\)" -S src/styles | sort | uniq | head -n 20
var(--bg)
var(--border)
var(--callout-accent)
var(--callout-bg)
var(--callout-border)
var(--danger-accent)
var(--danger-bg)
var(--danger-border)
var(--fg)
var(--link)
var(--muted)
var(--note-accent)
var(--note-bg)
var(--note-border)
var(--tip-accent)
var(--tip-bg)
var(--tip-border)
var(--warn-accent)
var(--warn-bg)
var(--warn-border)

Що це означає: У вас є інвентар змінних, які використовуються.

Рішення: Порівняйте це з тим, що визначено в :root. Відсутні визначення слід трактувати як помилки збірки, а не як «здається, нормально».

Завдання 8: Виявлення невизначених змінних пошуком відсутніх fallback-значень

cr0x@server:~$ rg -n "var\\(--(note|tip|warn|danger)-[a-z-]+\\)" src/styles | head
src/styles/callouts.css:14:background: var(--warn-bg);
src/styles/callouts.css:15:border-color: var(--warn-border);

Що це означає: Ці посилання не мають fallback-значень. Якщо токени відсутні або винесені поза скоуп, ви отримаєте порушені візуали.

Рішення: Для критичної семантики (ПОПЕРЕДЖЕННЯ/НЕБЕЗПЕКА) додавайте fallback-значення або контролюйте наявність токенів через тести (краще тести).

Завдання 9: Перевірте, що HTML містить мітки, а не лише іконки

cr0x@server:~$ rg -n "WARNING|DANGER|TIP|NOTE" build/ops -S | head
build/ops/runbook/index.html:216:<div class="title">WARNING</div>
build/ops/runbook/index.html:301:<div class="title">DANGER</div>

Що це означає: Серйозність присутня як текст у DOM.

Рішення: Якщо мітки відсутні, додавайте їх під час рендерингу. Стилі можуть змінюватися; текст — стійкий.

Завдання 10: Створіть сторінку «severity ladder» і переконайтеся, що вона збирається

cr0x@server:~$ ls -la src/pages/severity-ladder.md
-rw-r--r-- 1 cr0x cr0x 1482 Dec 28 10:22 src/pages/severity-ladder.md

Що це означає: У вас є єдина сторінка, що рендерить усі стани викликів для QA і візуальної регресії.

Рішення: Якщо такої сторінки немає — створіть її. Це канарка для регресій темування.

Завдання 11: Підтвердіть, що CI збирає той самий вихід локально (lockfile sanity)

cr0x@server:~$ node --version
v22.11.0

Що це означає: Версія Node вказана явно.

Рішення: Зафіксуйте версії Node у CI. Різниці у вихідному CSS через версії інструментів реальні і це тупий спосіб витрачати середу тижня.

Завдання 12: Проінспектуйте склад бандла, щоб побачити, де лежить CSS викликів

cr0x@server:~$ ls -lh build/assets | head
total 1.6M
-rw-r--r-- 1 cr0x cr0x  92K Dec 28 10:35 main-9a8c1.css
-rw-r--r-- 1 cr0x cr0x 610K Dec 28 10:35 vendor-a12ff.css
-rw-r--r-- 1 cr0x cr0x 120K Dec 28 10:35 main-9a8c1.js
-rw-r--r-- 1 cr0x cr0x 740K Dec 28 10:35 vendor-a12ff.js

Що це означає: CSS розділено; порядок завантаження має значення.

Рішення: Переконайтеся, що визначення токенів завантажуються перед компонентами, а компоненти — перед переоприділеннями. Якщо бандлер перевпорядковує, потрібні явні імпорти.

Завдання 13: Виявити випадкове дублювання CSS викликів у кількох бандлах

cr0x@server:~$ for f in build/assets/*.css; do echo "== $f"; rg -n "\.callout(\.|\[)" "$f" | head -n 3; done
== build/assets/main-9a8c1.css
1023:.callout.warn{--callout-accent:var(--warn-accent);--callout-bg:var(--warn-bg);--callout-border:var(--warn-border)}
1030:.callout.danger{--callout-accent:var(--danger-accent);--callout-bg:var(--danger-bg);--callout-border:var(--danger-border)}
== build/assets/vendor-a12ff.css
887:.callout{border:1px solid transparent;background:transparent}

Що це означає: У вас є базовий стиль викликів у vendor і мапінг серйозності в main.

Рішення: Це може бути нормально, але задокументуйте це. Якщо vendor зміниться, поведінка вашого компонента зміниться. Розгляньте перенесення базових стилів викликів у власний бандл для стабільності.

Завдання 14: Переконайтеся, що кольори посилань у викликах читаються в обох режимах

cr0x@server:~$ rg -n "a\\{|--link" src/styles -S
src/styles/tokens.css:14:  --link: #0b5fff;
src/styles/tokens.css:15:  --link-visited: #6a2cff;
src/styles/app.css:66:a{ color: var(--link); }

Що це означає: Посилання базуються на токенах, тож ви можете налаштовувати їх глобально, якщо фони викликів змінюються.

Рішення: Якщо посилання захардкоджені, перемістіть їх у токени. Саме в викликах люди клікають «Дізнатись більше» перед виконанням ризикованої дії.

План швидкої діагностики

Коли хтось повідомляє «виклики некоректні в темному режимі», ви хочете швидко знайти вузьке місце. Не елегантно. Швидко.

Перше: підтвердьте, що симптом семантичний, а не естетичний

  • Чи помітні ПОПЕРЕДЖЕННЯ і НЕБЕЗПЕКА з першого погляду?
  • Чи видно мітку?
  • Чи читається основний текст без масштабування?

Чому: Якщо семантика в порядку, можна запланувати полір. Якщо семантика порушена, ставте це як дефект високого пріоритету.

Друге: ідентифікуйте джерело істини для стану теми

  • Чи темний режим керується prefers-color-scheme?
  • Чи є ручний перемикач (data-theme, клас на html)?
  • Чи обидва активні й конфліктують?

Чому: «Темний режим неправильно» часто означає «у нас два темні режими».

Третє: перевірте наявність токенів і порядок перекриттів

  • Чи визначені --warn-* і --danger-* у :root?
  • Чи існують перевизначення для темного режиму в фінальному CSS?
  • Чи vendor CSS скидати компонент після ваших правил?

Чому: Якщо токени в порядку, але компонент перекривається пізніше — ваше виправлення в порядку завантаження, не в підборі кольору.

Четверте: перевірте помилки скоупінгу

  • Чи токени визначені під контейнером, який не включає контент документації?
  • Чи адмоніції рендеряться в iframe або shadow root?

Чому: CSS-змінні наслідуються. Якщо ваші виклики не всередині скоупу — вони не отримають значень.

П’яте: запустіть таргетований візуальний регрес для сторінки severity ladder

  • Порівняйте скриншоти світлого і темного режимів
  • Зробіть диф файлу токенів CSS

Чому: Візуальні регресії легко переконують. Дифи — ні.

Поширені помилки: симптом → корінь → виправлення

1) Симптом: НЕБЕЗПЕКА виглядає як ЗАМІТКА в темному режимі

Корінь: Темний режим використовує низькоопакові тонування для всіх викликів, але акценти мають занадто схожу люмінанс. Або альфа бордера надто низька, аби читатися на фоні сторінки.

Виправлення: Підвищіть контраст бордерів для вищих ступенів серйозності. Зробіть бордер НЕБЕЗПЕКИ сильнішим за ПОПЕРЕДЖЕННЯ. Розгляньте додавання тонкого лівого бордера або більш товстого бордера тільки для НЕБЕЗПЕКИ.

2) Симптом: фон виклику зникає на деяких сторінках

Корінь: Токени скоуплені до контейнера, який не присутній на цих сторінках (наприклад, токени задані на .docs-layout, а лендинг-сторінки використовують .marketing-layout).

Виправлення: Перенесіть семантичні токени у :root або забезпечте наслідування через спільний root-обгортник для обох макетів.

3) Симптом: Бордери прозорі, хоча CSS їх встановлює

Корінь: Інша таблиця стилів у каскаді пізніше ставить .callout { border-color: transparent; } або скидає бордери.

Виправлення: Виправте порядок імпортів. Якщо не можна, підвищте специфічність правила контрольовано (наприклад main .callout), а не використовуйте !important всюди.

4) Симптом: Тільки іконка пофарбована; все інше сіре

Корінь: Компонент застосовує лише --callout-accent, але не фон/бордер-токени, або відсутній мапінг класу серйозності на всі токени.

Виправлення: Замапте кожну серйозність на всі три токени: accent, background, border. Часткова темізація веде до неоднозначного UI.

5) Симптом: Посилання у викликах нечитаються в темному режимі

Корінь: Глобальні токени посилань розраховані для фонового кольору сторінки, а не для тонованих фонів викликів; контраст падає всередині викликів.

Виправлення: Налаштуйте глобальні токени посилань або встановіть токен посилань для викликів, напр.: .callout a{ color: var(--callout-link, var(--link)); }, з можливістю перевизначення для кожної серйозності.

6) Симптом: Друк/PDF втрачає розрізнення серйозності

Корінь: Залежність від тонувань фону і тільки кольору; стилі для друку часто вимикають фони.

Виправлення: Додайте CSS для друку: зберігайте бордери і мітки, розгляньте патерні бордери (суцільний vs подвійний) для високої серйозності.

7) Симптом: Адмоніції виглядають по-різному між MDX і markdown сторінками

Корінь: Різні рендерери виводять різний HTML, тож ваші селектори підходять лише під один варіант.

Виправлення: Нормалізуйте вивід на рівні рендерера або напишіть селектори, що покривають обидві структури навмисно (і тестуйте їх).

8) Симптом: Перемикач теми працює, але виклики не змінюються

Корінь: Токени встановлені лише в @media (prefers-color-scheme: dark), але ваш перемикач використовує html[data-theme="dark"].

Виправлення: Дзеркально відтворіть перевизначення під обидва механізми або, краще: уніфікуйте метод. Якщо потрібно підтримувати обидва, реалізуйте правило «перемикач переважає медіа-запит».

Контрольні списки / поетапний план

Крок за кроком: створити виклики, що витримують зміну тем

  1. Уніфікуйте розмітку: одна форма DOM для всіх адмоніцій (включно з міткою заголовка) у всіх типах контенту.
  2. Визначте базові токени у :root: фон, передній план, бордери, кольори посилань.
  3. Визначте семантичні токени серйозності: --note-*, --tip-*, --warn-*, --danger-*.
  4. Замапте серйозність на токени компоненту у компоненті виклику: задайте --callout-accent, --callout-bg, --callout-border.
  5. Реалізуйте fallback-значення в точці споживання компонента (бордери, фон), щоб відсутні токени деградували безпечно.
  6. Підтримайте темний режим з явними перевизначеннями під @media (prefers-color-scheme: dark) і/або вашим обраним механізмом перемикача.
  7. Додайте сторінку severity ladder, яка рендерить всі серйозності з посиланнями та блоками коду.
  8. Додайте CI-перевірку наявності токенів: парсити CSS або grep-ити збірку на наявність потрібних токенів.
  9. Додайте тести візуальної регресії для сторінки severity ladder у світлому та темному режимах.
  10. Визначте правила рев’ю: будь-яка зміна токенів вимагає скриншот-діфа та перевірки контрасту.

Операційний чекліст: перед мерджем зміни теми

  • ПОПЕРЕДЖЕННЯ та НЕБЕЗПЕКА візуально відрізняються в обох режимах без читання тіла тексту.
  • Мітки присутні й читаються.
  • Посилання всередині викликів залишаються читабельними і явно посиланнями.
  • Стилі для друку зберігають бордери та мітки.
  • Оновлення vendor CSS не можуть мовчки переоприділити токени (перевірено порядок імпортів).
  • Стан темного режиму має одне джерело істини (або документоване правило пріоритету).
  • Сторінка severity ladder проходить візуальний регресійний тест.
ПОПЕРЕДЖЕННЯ

Якщо ваша «дефініція готовності» для темізації документації — «виглядає нормально на моєму ноуті», у вас немає дефініції готовності. У вас є враження.

Часті питання

1) Чи використовувати для викликів <aside> чи <div>?

Використовуйте <aside>, коли вміст є побічним до основного потоку (а багато викликів такими і є). Використовуйте <div>, якщо ваш генератор не може надійно виводити <aside>. Послідовність важливіша за семантичну досконалість.

2) Темізувати виклики за допомогою hex чи HSL?

Використовуйте те, що ваша команда може підтримувати. HSL полегшує регулювання люмінансу для темного режиму, але лише якщо команда ним користується. Токени — головне, не формат кольору.

3) Чи достатньо prefers-color-scheme, чи потрібен ручний перемикач?

Для публічної документації prefers-color-scheme — гарний дефолт. Ручний перемикач корисний, коли люди працюють у змішаних середовищах (презентації, шаринг екрану) або коли системні налаштування заблоковані. Якщо додаєте перемикач, зробіть його таким, щоб він передбачувано переважав медіа-запит.

4) Як запобігти тому, щоб оновлення залежності ламали кольори викликів?

Два важелі: (1) тримайте токени у вашому репозиторії, а не у вендорській темі, яку ви не контролюєте; (2) додавайте в CI візуальні регресії і перевірки наявності токенів. Ставте зміни токенів як зміни API.

5) Чому не просто використовувати !important для зупинки перекриттів?

Бо ви виграєте битву і програєте війну. !important масштабно погано росте і ламає розширюваність тем. Виправте порядок і скоупування спершу. Якщо вже мусите, застосовуйте його до токенів у корені, а не до кожної властивості в кожному компоненті.

6) Чи потрібні різні іконки для кожної серйозності?

Так, якщо ви хочете, щоб користувачі швидко відрізняли серйозність. Щонайменше ПОПЕРЕДЖЕННЯ і НЕБЕЗПЕКА не повинні мати ту саму іконку. Поєднуйте іконку + мітку + колір для надлишковості.

7) Яке мінімальне покриття тестами для викликів?

Принаймні одна сторінка, яка рендерить усі серйозності в обох режимах, під візуальну регресію. Додайте просту текстову перевірку на наявність токенів у збудованому CSS. Це дешево і ловить клас помилок «файл токенів не імпортовано».

8) Як обробляти виклики всередині вкладених контейнерів (вкладки, акордеони, сайдбари)?

Переконайтеся, що змінні визначені у :root або наслідуються в ті контейнери. Уникайте скоупування токенів до макета, який не всі сторінки використовують. Якщо контейнер свідомо змінює фон, розгляньте специфічні налаштування через додаткові токени (але зберігайте узгоджене відображення серйозності).

9) Чи потрібні тонувані фони для викликів?

Ні. Бордери плюс чітка мітка часто достатні і краще друкуються. Тоновані фони — допомога для читабельності, а не основний сигнал серйозності.

10) Як зберегти консистентність викликів між кількома сайтами документації?

Поширюйте один CSS-компонент виклику і файл токенів. Дозвольте кожному сайту перевизначати токени, а не правила компоненту. Так ви отримаєте гнучкість бренду без семантичного розмивання.

Висновок: наступні кроки, які працюють

Якщо ви хочете, щоб виклики не підводили вас, ставтеся до них як до операційного інтерфейсу, а не до декору. Ви кодуєте ризик. Зробіть це кодування стабільним.

  1. Уніфікуйте розмітку адмоніцій, щоб ваші селектори не гадали.
  2. Перенесіть кольори серйозності у CSS-змінні (семантичні токени) і замапте їх у єдиний компонент виклику.
  3. Реалізуйте темний режим як явні перевизначення токенів, а не як випадковий побічний ефект.
  4. Додайте сторінку severity ladder і включіть її в CI з візуальним дифом.
  5. Прийміть нудне правило: зміни токенів вимагають рев’ю як зміни коду, бо вони такими і є.

Коли UI документації правильно передає ризик, користувачі роблять менше припущень. Менше припущень — менше інцидентів. Математика неприємна, але послідовна.

← Попередня
PostgreSQL vs OpenSearch: гібридна пошукова конфігурація, яка справді працює
Наступна →
Розігрів домену для електронної пошти: практичний план, щоб уникнути миттєвих позначок як спам

Залишити коментар