01
Интеграция и Scoped-стили
Изоляция стилей блока от глобальных стилей конструктора — первостепенная задача при генерации любого компонента.
BEM-префикс блока
Каждый генерируемый блок обязан иметь уникальный двух-трёхсимвольный префикс, используемый в BEM-именовании. Префикс выбирается однократно при создании блока и применяется ко всем CSS-классам внутри.
- Формат:
.prefix-block__element--modifier
- Пример:
.ah-hero__title--accent, .vx-card__body
- Вложенность — не более одного уровня:
.ah-card__title, а не .ah-card__header__title
- Внутренний элемент продвигается в самостоятельный BEM-элемент блока
- Запрещено: использование стандартных имен вроде
.title, .btn, .container без префикса
Необходимый сброс блока
Каждый блок должен начинать свой <style> с двух обязательных правил:
CSS
/* Скоупинг box-sizing */
.vx-card, .vx-card * { box-sizing: border-box; }
/* Наследование шрифта формами */
.vx-card input, .vx-card textarea, .vx-card select {
font: inherit;
}
Почему это важно
Браузеры применяют к <input>, <textarea> и <select> встроенный шрифт, который игнорирует наследование. Без явного font: inherit формы будут отображаться шрифтом по умолчанию.
Изоляция через замыкания
- Весь JavaScript обёрнут в IIFE:
(function() { ... })();
- Корневой элемент блока ищется через
document.currentScript?.closest('section')
- Все DOM-запросы и привязки событий — только внутри
root
- Запрещено:
document.querySelector, document.getElementById, document.querySelectorAll вне root
- Запрещено: создание глобальных переменных и переиспользуемых ID
JS
(function() {
const root = document.currentScript?.closest('section');
if (!root) return;
// Все запросы — относительно root
const btn = root.querySelector('.ah-card__button');
btn.addEventListener('click', handler);
})();
02
Дизайн-токены платформы
Все визуальные решения должны ссылаться на глобальные CSS-переменные платформы. Хардкод значений запрещён.
Цветовые токены
| Токен |
CSS-переменная |
Назначение |
| Primary |
var(--bzm-ai-color-style-1) |
Заголовки, основной текст, кнопки |
| Secondary |
var(--bzm-ai-color-style-2) |
Основной текст, описания |
| Accent |
var(--bzm-ai-color-style-3) |
Акценты, hover-состояния, иконки |
| Surface |
var(--bzm-ai-color-style-4) |
Фоны, карточки, разделители |
Типографические токены
| Стиль |
Размер (desktop) |
Размер (mobile) |
Line-height |
| Display |
var(--bzm-ai-font-size-style-1-desktop) |
var(--bzm-ai-font-size-style-1-mobile) |
var(--bzm-ai-line-height-style-1-*) |
| Heading |
var(--bzm-ai-font-size-style-2-desktop) |
var(--bzm-ai-font-size-style-2-mobile) |
var(--bzm-ai-line-height-style-2-*) |
| Lead |
var(--bzm-ai-font-size-style-3-desktop) |
var(--bzm-ai-font-size-style-3-mobile) |
var(--bzm-ai-line-height-style-3-*) |
| Body |
var(--bzm-ai-font-size-style-4-desktop) |
var(--bzm-ai-font-size-style-4-mobile) |
var(--bzm-ai-line-height-style-4-*) |
| Caption |
var(--bzm-ai-font-size-style-5-desktop) |
var(--bzm-ai-font-size-style-5-mobile) |
var(--bzm-ai-line-height-style-5-*) |
Шрифтовые гарнитуры
- Заголовки:
var(--bzm-ai-font-heading-family)
- Тело:
var(--bzm-ai-font-body-family)
- Шрифт ставится на общий контейнер через наследование, не на каждый элемент
Layout-токены
- Максимальная ширина контента:
var(--bzm-ai-content-width)
- Паддинги секций:
var(--bzm-ai-padding-style-1-top/bottom)
- Мобильные паддинги:
var(--bzm-ai-padding-style-1-mobile-top/bottom)
Кнопки
- Primary CTA:
button_style_1
- Secondary:
button_style_2
- Tertiary:
button_style_3
- Структура:
<a class="button button_style_1 prefix__btn"><span class="button__inner"><span class="button__text">...</span></span></a>
⚠️ Запрет на хардкод
Значения вроде #333, 16px, Inter, sans-serif недопустимы. Все визуальные свойства обязаны ссылаться на var(--bzm-ai-*) токены.
03
Адаптивность и кроссбраузерность
Все блоки должны корректно отображаться от 320px до 1440px+ и более широких экранов.
Поддерживаемые брейкпоинты
| Диапазон |
Поведение |
| 320–480px |
Полный вертикальный стак, максимальная читаемость |
| 480–640px |
Вертикальный стак, увеличенные отступы |
| 640–960px |
Переход к 2-колоночным сеткам, адаптивные изображения |
| 960–1200px |
Полная desktop-раскладка, уплотнение |
| 1200px+ |
Максимальная ширина по var(--bzm-ai-content-width)
|
Fluid-типографика с clamp()
- Используйте
clamp() для плавного масштабирования между mobile и desktop значениями
- Формула:
clamp(var(--mobile-size), fluid-calc, var(--desktop-size))
- Промежуточное значение рассчитывается через
vw-единицы
CSS
font-size: clamp(
var(--bzm-ai-font-size-style-2-mobile),
4vw,
var(--bzm-ai-font-size-style-2-desktop)
);
Safe-area и reduced-motion
- Для фиксированных элементов (нижние панели, sticky-хедеры) учитывайте
env(safe-area-inset-bottom)
- Все CSS-переходы и анимации оборачивайте в
@media (prefers-reduced-motion: reduce)
- При
prefers-reduced-motion: переходы и анимации устанавливаются в none
- Избегайте горизонтального скролла, если он не запрошен явно
💡 Практический совет
Тестируйте адаптивность в DevTools с включённым эмулятором touch-событий. Мобильное поведение часто отличается от десктопного ресайза.
04
Производительность и оптимизация
Каждый блок должен оптимизировать загрузку ресурсов и рендеринг для обеспечения высоких Core Web Vitals.
Изображения
- Все изображения за пределами первого экрана:
loading="lazy"
- Все изображения:
decoding="async"
- Изображения первого экрана:
loading="eager" и fetchpriority="high"
- Указывайте
width и height для предотвращения CLS (сдвига макета)
- Скрывайте изображения без
src: img:not([src]) { visibility: hidden; }
- Используйте
srcset и sizes для адаптивных изображений
Рендеринг и содержимое
- Секции вне вьюпорта:
content-visibility: auto для отложенного рендеринга
- Указывайте
contain-intrinsic-size вместе с content-visibility
- Не более 1-2 осмысленных CSS-переходов на блок
- Запрещены: Intersection Observer для декоративных анимаций, анимации при загрузке без функциональной цели
Работа с DOM
- Используйте
textContent вместо innerHTML при вставке пользовательских данных
- Пакетные DOM-операции: соберите фрагмент через
DocumentFragment, затем одна вставка
- Кэшируйте результаты
querySelector в переменные
- Запрещено:
innerHTML с данными из пользовательского ввода
05
UX и Доступность (A11y)
Каждый блок должен быть полностью доступен для навигации с клавиатуры и понятен экранным ридерам.
Семантика HTML5
- Корневой элемент блока —
<section>
- Заголовки иерархические:
<h1> → <h2> → <h3> без пропусков
- Навигация:
<nav aria-label="...">
- Списки:
<ul>/<ol> с <li>, а не div-ы
- Списки стилей:
padding-left: 22px для ol, padding-left: 20px для ul
Фокус и клавиатурная навигация
- Все интерактивные элементы имеют стили для
:focus-visible
- Фокус-трап для модальных окон: фокус циклически замкнут внутри модалки
-
Escape закрывает модальные окна
- Порядок табуляции логичен и соответствует визуальному порядку
- Телефонные ссылки наследуют цвет текста
ARIA и динамический контент
- Динамически обновляемые области:
aria-live="polite" или aria-live="assertive"
- Все интерактивные элементы без видимого текста:
aria-label
- Иконки-кнопки:
aria-label="Описание действия"
- Скрытый декоративный контент:
aria-hidden="true"
- Состояния:
aria-expanded, aria-selected, aria-checked для toggle-элементов
Формы
- Каждый
<input>, <textarea>, <select> имеет <label>
- Label оборачивает контрол или связан через
for/id
- Используйте нативную валидацию:
required, type="email", type="tel"
- Группы радиокнопок/чекбоксов:
<fieldset> + <legend>
- Значения опций — человекочитаемые и на языке страницы
06
Безопасность
Весь код должен быть безопасен для встраивания в существующую страницу и не создавать векторов атак.
Запрещённые конструкции
- Запрещено:
eval()
- Запрещено:
innerHTML с пользовательским вводом
- Запрещено: inline event handlers (
onclick="...")
- Запрещено: динамическая инъекция скриптов
- Запрещено: загрузка внешних скриптов, стилей, шрифтов, CDN
- Запрещено: доступ к
cookies, localStorage, sessionStorage
- Запрещено: использование браузерных API, не связанных с UI
URL и History API
- Валидация URL перед
history.replaceState/pushState
- Проверка на
javascript: и data: протоколы
- Использование
try/catch при парсинге URL
Внешние ресурсы
- Внешние
<iframe>: обязательный атрибут sandbox
- Внешние ссылки:
rel="noopener noreferrer"
- Весь пользовательский текст обрабатывается как plain text
⚠️ Критическое правило
Блок должен быть полностью самодостаточным и безопасным для встраивания в любую существующую страницу. Никаких побочных эффектов на глобальное состояние.
07
Архитектура и контроль версий
Код должен быть модульным, документированным и предсказуемым. Каждое решение — осознанное.
Модульность
- Один блок = один
<section>, один <style>, один <script>
- JavaScript — всегда IIFE, без глобальных переменных
- Блок не зависит от других блоков и не требует их наличия
- Все зависимости описаны явно (токены, структура DOM)
Код-стайл
- Без «магических чисел» — каждое значение имеет именованную переменную или комментарий
- JSDoc для сложных функций: описание, параметры, возвращаемое значение
- Комментарии на языке страницы (locale сайта)
- Именование переменных — camelCase, константы — UPPER_SNAKE_CASE
JS
/**
* Переключает видимость выпадающего меню
* @param {HTMLElement} trigger - Кнопка-триггер
* @param {HTMLElement} menu - Элемент меню
* @returns {void}
*/
function toggleDropdown(trigger, menu) {
const isOpen = menu.classList.toggle('is-open');
trigger.setAttribute('aria-expanded', String(isOpen));
}
Тестирование перед публикацией
- Визуальная проверка на всех 5 брейкпоинтах
- Клавиатурная навигация: Tab, Enter, Escape, стрелки
- Проверка с включённым
prefers-reduced-motion
- Валидация HTML (нет дублирующихся ID, закрытые теги)
- Проверка контрастности текста (WCAG AA минимум)
- Тестирование в режиме инкогнито (без кеша)
💡 Чек-лист генерации
Перед отправкой каждого блока пройдитесь по этому списку: (1) BEM-префикс, (2) блочный сброс, (3) токены без хардкода, (4) IIFE-замыкание, (5) адаптивность 320px–1440px+, (6) ARIA-разметка, (7) lazy-loading, (8) нет eval/innerHTML.