code6 · дизайн-система «Federal»
Единая система дизайна
Форум, база знаний, TERMINAL, страница приёма и лендинги живут в разных технологиях, но должны опознаваться как один продукт. Обеспечивается это не одинаковой вёрсткой, а общими именами токенов, одним шрифтом, одним набором иконок и едиными правилами применения.
Логотип
Варианты
| Вариант | Превью | Файл | Скачать | Когда применяется | Минимальная ширина |
|---|---|---|---|---|---|
| Основной |
SVG 1057 × 217 · PNG 2000 × 411 |
code6_logo.c6LogoFull |
Шапки сайтов, презентации, обложки, промо | 120 px · 30 мм | |
| Сокращённый |
SVG 801 × 217 · PNG 2000 × 542 |
code6_logo-short.c6LogoShort |
Узкие шапки, соцсети, оверлеи | 96 px | |
| Знак, скобки |
SVG 208 × 217 · PNG 1917 × 2000 |
code6_logo-mark.c6LogoMark |
Фавикон, иконка приложения, водяной знак, индикатор загрузки | 16 px | |
| Маскот Кодик | code6_codic.pngcode6_codic_glasses.png | Пустые состояния, ошибки, приветствия, стикеры. Не является заменой логотипа | 64 px |
Фоны
Допустимые подложки: белая, светлое полотно, --c6ColorBlack и --c6ColorBlue. На изображениях логотип ставится поверх затемнения не менее 40 процентов или в чистой зоне кадра. На --c6ColorRed логотип не размещается, подпись бренда на нём читается как ошибка системы.
Три исполнения
Логотип должен встраиваться как SVG и перекрашиваться классами вместе со схемой. PNG используется только, когда мы не можем управлять кодом.
Одноцветное исполнение
Монохром нужен для сред, в которых цвет логотипа задаётся внешними условиями или фирменная палитра мешает читаемости. Класс c6LogoMono переводит все части знака на currentColor, сохраняя его форму и позволяя использовать один цвет без создания отдельной версии файла.
Логотип на изображениях
На изображении логотип ставится только поверх затемнения и только в одноцветном белом исполнении.
Охранное поле и размеры
Пунктир — минимальное свободное поле: высота знака «6» с каждой из четырёх сторон.
| Поле | Значение |
|---|---|
| Шаг сетки | 16 — толщина всех штрихов. Все зазоры кратны ему |
| Межбуквенный зазор | 32 — два шага |
| Зазор между знаком и словом | 64 — четыре шага, вдвое больше межбуквенного |
| Минимум на экране | 120 px основной, 48 px сокращённый, 16 px мини |
| Минимум в печати | 30 мм по ширине для основного |
| Формат | Inline-SVG в разметке. PNG — только там, где разметку не контролируем: письма, сторонние площадки |
| Перекраска | Токенами --c6LogoInk, --c6LogoLead, --c6LogoTrail. Значения fill в файле не правятся |
| Написание названия | code6.ru приоритетно, допустимы code6 и код6. Всегда со строчной буквы. Других написаний нет |
| Канон и алиас | В заголовках и метаданных — латинское написание, кириллическое рядом в скобках. |
Префикс c6 | Язык кода: токены, классы, имена файлов. Именем бренда не является и в интерфейсе не показывается |
| Шрифт логотипа | Atlantis International. В интерфейсе не применяется |
Что с логотипом не делают
- Не перекрашивают: только фирменное, белое или чёрное исполнение.
- Не растягивают, не наклоняют, не добавляют тень, обводку и градиент.
- Не разбирают на части и не собирают новые композиции из знака и вордмарка.
- Не набирают название интерфейсным шрифтом там, где должен стоять полный логотип.
- Не ставят к другим логотипам ближе охранного поля.
Цвет
Бренд
Четыре цвета. Чёрный и белый являются основными, синий отвечает за действия на страницах, красный — за акцент и предупреждение.
Чёрный
--c6ColorBlack · #0F0F0F
Белый
--c6ColorWhite · #FFFFFF
Синий
--c6ColorBlue · #1B6FE0
Красный
--c6ColorRed · #C8102E
--c6ActionFill используется для кнопок, активных переключателей и заполнения прогресса. --c6ActionInk — для ссылок и текстовых действий. Если элемент только сообщает состояние, он берёт статусный токен, а не синий цвет действия.Действия и статусы
Синий связан с действием, переходом или активным процессом. Статусы описывают результат или состояние и не меняются при смене акцента: одно значение — один смысл на всех поверхностях.
| Токен | Светлая | Тёмная | Значение |
|---|---|---|---|
--c6ActionFill | #1B6FE0 | #4A93FF | Главное действие, активный выбор, заполнение прогресса |
--c6StatusWaiting | #B8791A | #E0A33C | Ожидание, срок на исходе, ресурс заканчивается |
--c6StatusSuccess | #037A58 | #1FC08E | Одобрено, пройдено, набор открыт |
--c6StatusDanger | #C8102E | #FF6B75 | Отказ, блокировка, стоп-фактор |
--c6StatusPro | #7A3EA1 | #B98AE0 | Подписка Boosty: приоритет, объединения, платные механики |
--c6TextPrimary, то цветом текста становится --c6SurfaceRaised — и никак иначе. Токен --c6TextOnInverse здесь не подходит: он про тёмную плашку подвала, которая тёмная в обеих схемах, поэтому в тёмной схеме даёт белое по белому.Поверхности и текст
| Токен | Светлая | Тёмная | Назначение |
|---|---|---|---|
--c6SurfaceCanvas | #F1F3F7 | #131A24 | Полотно страницы |
--c6SurfaceRaised | #FFFFFF | #1A2130 | Карточки и панели: всегда светлее полотна |
--c6SurfaceSunken | #F7F8FC | #141B25 | Поля ввода, блоки кода |
--c6SurfaceInverse | #212936 | #0C1119 | Подвал, плашки медиа |
--c6TextPrimary | #212936 | #E8ECF3 | Заголовки и основной текст |
--c6TextSecondary | #5C6377 | #A2ACBD | Абзацы и описания |
--c6TextFaint | #667085 | #8A94A6 | Подписи и метаданные |
--c6TextDisabled | #8A91A3 | #6E7A8C | Выключенные элементы, недоступные подписи, неактивные иконки |
--c6BorderDefault | #E1E5EE | #28313F | Границы карточек и секций |
Департаменты
Доменная подпалитра под префиксом c6. Цвет опознаёт службу и живёт только в шапке карточки, полосе, маркере списка или бейдже с аббревиатурой. Карточка остаётся нейтральной, ссылки внутри остаются синими.
CIV · --c6DepartmentCiv
LSPD / LSSD · --c6DepartmentLspd
SAHP · --c6DepartmentSahp
LSCoFD · --c6DepartmentFd
FIB · --c6DepartmentFib
DOT · --c6DepartmentDot
DD · --c6DepartmentDd
Связь · --c6DepartmentComms
Где брать ступень
У каждого департамента есть несколько ступеней одного цвета. Выбирается не новый оттенок, а подходящая ступень из уже заданной подпалитры.
| Контекст | Кандидат | Участник | Командование |
|---|---|---|---|
| Сайт, светлая схема | Deep, белый текст | Base, белый текст | Light, тёмный текст |
| Сайт, тёмная схема | Base | Light | Bright |
| Discord | Light | Bright | Bright, секция и иконка |
Правила подпалитры
| Правило | Значение |
|---|---|
| Где живёт цвет | Шапка карточки и маркер списка. Заливкой карточки, текстом и кнопками не бывает |
| Столкновения | Ни одно значение не совпадает с семантическим токеном и не лежит рядом с ним |
| Гражданский | Зелёный, равноправный со службами. Показывается с плашкой везде, включая развилку на лендинге |
| Цвет не один | Рядом с плашкой всегда аббревиатура или название. Цвет опознаёт быстрее, но информацию несёт текст |
| Слитые департаменты | LSPD и LSSD в US — один департамент. --c6DepartmentLssd оставлен алиасом на --c6DepartmentLspd |
| Текст на плашке | На тёмной ступени используется белый текст, на светлой — основной текстовый токен |
| Тёмная схема | Берётся следующая, более светлая ступень той же подпалитры |
| Новый департамент | Добавляется только как новая доменная подпалитра --c6Department*, без заимствования статусных цветов |
Именование
Federal — имя дизайн-системы продуктов code6, но не технический namespace. В коде всё живёт под единым префиксом c6: базовые значения, семантические токены и компоненты. Семантический токен может ссылаться на базовый, но обратное направление запрещено: иначе смена акцента потребует правки каждого компонента.
| Слой | Префикс | Отвечает на вопрос | Примеры |
|---|---|---|---|
| Базовое значение | --c6Color*, --c6Department* | Какой это цвет или доменная подпалитра? | --c6ColorBlue, --c6ColorRed, --c6DepartmentLssd |
| Семантический токен | --c6Surface*, --c6Text*, --c6Action* | Для чего это используется? | --c6SurfaceCanvas, --c6TextPrimary, --c6ActionFill |
| Компонент | .c6* | Где применяется? | .c6ButtonPrimary, .c6StatusPillWaiting, .c6DepartmentBandSahp |
:root {
--c6ColorBlue: #1B6FE0; /* базовое значение */
--c6ActionFill: var(--c6ColorBlue); /* семантический токен */
}
.c6ButtonPrimary { /* компонент читает семантику */
background: var(--c6ActionFill);
color: var(--c6TextOnFill);
}
card, btn, row, badge. Любой наш класс без префикса рано или поздно столкнётся с его обновлением. Префикс c6 снимает вопрос навсегда и заодно делает видимым, где в разметке наш компонент, а где утилита фреймворка.Поверхности
Схема — свойство поверхности, а не продукта. Форум и TERMINAL тёмные по своей природе: первый показывает фоновое видео и рендеры, второй имитирует служебную систему, в которую смотрят часами. База знаний и страница приёма светлые: там читают длинные тексты и принимают решения. Требовать от всех одной схемы бессмысленно, требовать одинаковых токенов обязательно.
| Поверхность | Технология | Схема | Особенности |
|---|---|---|---|
| Форум | Invision Community | тёмная | Фоновое видео и рендеры, максимальная плотность навигации |
| База знаний | Docs | обе, тёмная по умолчанию | Длинные тексты, переключатель схемы у пользователя |
| TERMINAL и Элка | внутренняя CAD/MDT | тёмная | Плотные таблицы, статусы вызовов, долгие сессии |
| Приём | PHP и Bootstrap | обе, светлая по умолчанию | Воронка состояний, один целевой сценарий на экран |
| Лендинги и медиа | разное | по задаче | Обязательны шрифт, иконки и палитра |
Типографика
Inter Variable — единственный интерфейсный шрифт на всех поверхностях. Переменное начертание даёт весь диапазон толщин одним файлом и оптический размер, за счёт которого мелкий текст остаётся читаемым, а крупный не выглядит рыхлым. Atlantis International — только логотип и вордмарк.
@font-face {
font-family: "Inter";
src: url("/fonts/InterVariable.woff2") format("woff2-variations");
font-weight: 100 900;
font-display: swap;
}
:root {
--c6FontSans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
--c6FontMono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--c6FontLogo: "Atlantis International", var(--c6FontSans);
}
Шкала
Девять ступеней, ни одной сверх. Крупные кегли адаптивны через clamp() и не требуют брейкпоинтов; мелкие фиксированы, потому что уменьшать их на мобильном нельзя.
| Токен | Десктоп | Мобильный | Интерлиньяж | Вес | Где |
|---|---|---|---|---|---|
--c6FontSizeDisplay | 40 | 30 | 1.12 | 600 | Первый экран лендинга |
--c6FontSizeH1 | 32 | 26 | 1.18 | 600 | Заголовок страницы |
--c6FontSizeH2 | 24 | 21 | 1.25 | 600 | Заголовок блока |
--c6FontSizeH3 | 19 | 17 | 1.35 | 600 | Заголовок карточки |
--c6FontSizeLead | 17 | 17 | 1.6 | 400 | Подводка под заголовком |
--c6FontSizeBody | 15 | 15 | 1.6 | 400 | Основной текст, кнопки |
--c6FontSizeSmall | 13 | 13 | 1.55 | 400 | Текст в карточках, ссылки |
--c6FontSizeCaption | 12 | 12 | 1.5 | 400 | Подписи, метаданные |
--c6FontSizeMicro | 11 | 11 | 1.45 | 500 | Надзаголовки, капслок |
Здесь всё решают люди
Заголовок страницы
Заголовок блока
Заголовок карточки
Подводка под заголовком — 17 пунктов, вторичный цвет
Основной текст интерфейса. Длина строки ограничена 74 знаками, дальше глаз теряет начало следующей.
Текст внутри карточек и списков
Подписи, даты, счётчики
Надзаголовок секции
7KQ2MX4B1P — моноширинный только для кодов и ключей
Обязательная адаптивность
| Правило | Значение |
|---|---|
| Минимальный кегль | 12 px. Ниже нельзя нигде, включая подписи и сноски |
| Основной текст на мобильном | Не меньше 15 px. Уменьшение на узком экране запрещено |
| Цель нажатия | Не меньше 44 × 44 px, включая ссылки в плотных списках |
| Длина строки | 60–78 знаков. На широком экране ограничивается max-width, а не растягивается |
| Крупные кегли | Только через clamp(), без брейкпоинтов и голых vw |
| Веса | 400 — текст, 500 — интерфейс и микро, 600 — заголовки. 300 и тоньше не применяются: на тёмной схеме разваливаются |
| Горизонтальный скролл | Отсутствует на любой ширине от 320 px |
Иконки
Lucide — единственный иконочный пак. Он контурный, с постоянной толщиной обводки и сеткой 24, поэтому не спорит с Inter и одинаково ведёт себя в обеих схемах. Смешивать паки нельзя: разная толщина линии заметна даже неподготовленному глазу и читается как небрежность.
| Правило | Значение |
|---|---|
| Размеры | 14, 16, 18, 20, 24. Других нет |
| Соответствие тексту | Иконка рядом с текстом равна его интерлиньяжу: 16 к тексту 13, 20 к тексту 15 |
| Толщина обводки | 2 по умолчанию, 1.75 для размера 24 и выше |
| Цвет | Всегда currentColor. Собственного цвета у иконки нет |
| Смысл | Одна иконка — одно значение во всех продуктах. Список пар ведётся вместе с системой |
| Доступность | Декоративная — aria-hidden="true". Несущая смысл — с текстовой подписью рядом |
<i data-lucide="shield" width="20" height="20" aria-hidden="true"></i>
// После каждой перерисовки DOM иконки нужно пересоздать,
// иначе новые узлы останутся пустыми плейсхолдерами.
function renderScreen(screenName) {
pageElement.innerHTML = buildScreenMarkup(screenName);
if (window.lucide) {
window.lucide.createIcons();
}
}
Спейсинг и сетка
Базой берётся шкала Bootstrap. Она покрывает почти все случаи утилитами и избавляет от самодельных отступов, которые расходятся между страницами. Расстояния задаются в разметке утилитами, а не в CSS: p-4, px-lg-5, g-3, gap-2, mt-3. Токены --c6Space* нужны там, где утилита неприменима — внутри компонента.
| Токен | Утилита | Значение | Где применяется |
|---|---|---|---|
--c6Space1 | 1 | 4 px | Между иконкой и подписью, внутри чипа |
--c6Space2 | 2 | 8 px | Между строками карточки, gap в списках чипов |
--c6Space3 | 3 | 16 px | Между карточками в ряду, отступ абзаца |
--c6Space4 | 4 | 24 px | Поля карточек и секций на мобильном |
--c6Space5 | 5 | 48 px | Поля секций на десктопе |
| Правило | Значение |
|---|---|
| Секция | p-4 p-lg-5 — 24 px на мобильном, 48 px на десктопе |
| Карточка | p-3, крупная — p-4 |
| Ряды | row и g-3. Собственные grid-контейнеры только там, где Bootstrap не выражает раскладку |
| Вертикальный ритм | Отступ между блоками кратен 8. Значения вне шкалы не вводятся |
| Порядок утилит | Раскладка, отступы, оформление: d-flex justify-content-between align-items-center px-4 py-3 |
Компоненты
Полный набор с вариациями собран в интерфейсе приёма. Здесь базовые — те, что обязаны выглядеть одинаково на всех поверхностях, и решения, из которых они выросли.
Кнопки
Статусы
Карточки и прогресс
Базовая
Поверхность на ступень выше полотна, тонкая граница, радиус 12.
Акцентная
Двухпиксельная синяя рамка вместо заливки — вес без введения пятого цвета.
Проблесковые огни
Рисунок вспышек повторяет настоящую мигалку: синяя сторона даёт две короткие вспышки, затем две даёт красная. Это не декоративный пульс, а узнаваемый ритм — он опознаётся боковым зрением и работает даже на четырёх пикселях высоты.
Полоса
Основной вариант. Ставится над шапкой страницы, над первым экраном или как разделитель перед важным блоком.
На светлом
На чёрном — свечение сильнее, приглушённое состояние темнее
Пара точек
Компактный вариант для строки статуса: смена идёт, сервер онлайн, трансляция в эфире. Рядом обязателен текст — сами по себе точки значения не несут.
Бегущий отблеск
Индикатор процесса: загрузка страницы, ожидание ответа сервера, отправка формы. Заменяет обычный прогресс-бар там, где длительность неизвестна.
Фоновое свечение
Самый тихий вариант: два размытых пятна по краям, медленно дышащих в противофазе. Для первого экрана и пустых состояний. Под текстом допустим только в этом виде — размытие 60 пикселей и низкая непрозрачность не мешают читать.
Здесь всё решают люди
Отбор проходит каждый
Разметка
<div class="c6LightBar" role="presentation" aria-hidden="true"> <span class="c6LightBarSegment"></span> <span class="c6LightBarSegment"></span> </div> <span class="c6LightDots" role="presentation" aria-hidden="true"> <i class="c6LightDot c6LightDotLead"></i> <i class="c6LightDot c6LightDotTrail"></i> </span> <div class="c6LightSweep" role="presentation" aria-hidden="true"></div> <section class="c6LightAmbient"> ... первый экран ... </section>
Правила огней
| Правило | Значение |
|---|---|
| Частота | 2.5 вспышки в секунду. Порог WCAG 2.3.1 — три в секунду, превышать нельзя ни при каких обстоятельствах |
| Количество | Один элемент огней на экран. Два — уже аттракцион |
| Соседство | Не размещается рядом с чипами статуса, полем ошибки, карточкой отказа |
| Под текстом | Только фоновое свечение. Полоса и точки текст не подложены никогда |
| Порядок цвета | Синий ведёт, красный отвечает. Обратный порядок не применяется |
| Доступность | aria-hidden="true" и role="presentation". Смысл несёт текст рядом, не анимация |
| Снижение движения | При prefers-reduced-motion анимация останавливается, огни остаются статичными |
Где применяется
- Полоса над шапкой сайта и над первым экраном лендинга.
- Точки в строке «смена идёт» на форуме и в TERMINAL.
- Отблеск как индикатор загрузки вместо спиннера.
- Свечение на первом экране и в пустых состояниях.
- Заставки видео, обложки, оверлеи трансляций.
Где не применяется
- В форме подачи заявки и на экранах статуса кандидата.
- Рядом с сообщением об ошибке или отказе.
- В плотных таблицах TERMINAL — там мигание конкурирует с реальными статусами вызовов.
- В письмах: анимация в почте не поддерживается, а статичная полоса выглядит как артефакт.
Логотип и маскот
У знака четыре исполнения и у каждого своя роль в движении. Основной логотип появляется, мини-логотип работает индикатором, маскот живёт в пустых состояниях. Общее ограничение одно: логотип нельзя искажать, поэтому анимации используют только непрозрачность, смещение и равномерный масштаб — ни перекоса, ни разного растяжения по осям.
Появление логотипа
Логотип поднимается на восемь пикселей и проявляется, следом под ним включаются огни. Порядок важен: сначала знак, потом мигалка — иначе внимание уходит на движение, а не на бренд.
Мини-логотип как индикатор
Самый узнаваемый индикатор из возможных: это не абстрактное кольцо, а собственный знак. Применяется там, где спиннер выглядел бы безлико — при первой загрузке страницы, в модальном окне, на заставке.
Скобки в движении
Развитие того же приёма: половины знака расходятся и сходятся, пока идёт работа. Здесь настоящие пути из code6_logo-mini.svg — именно потому, что знак живёт в разметке, а не картинкой, его половины можно двигать по отдельности.
Маскот
Кодик закрывает пустые состояния и технические экраны. Версия в очках закреплена за машинными контекстами: рядом со сгенерированным блоком и на долгом ожидании ответа модели. Это удобная договорённость — по одной картинке видно, что отвечает система, а не человек.
Спокойное парение · пустые состояния
Ускоренное парение · система думает
Пустое состояние целиком
Пока ничего нет
Записи появятся, когда пройдёт первая смена
Правила
| Правило | Значение |
|---|---|
| Свойства анимации | Только opacity, translate и равномерный scale. Перекос и неравномерное растяжение искажают знак |
| Маскот и логотип | Кодик логотип не заменяет и рядом с ним в одной связке не ставится |
| Очки | Версия в очках закреплена за машинными контекстами. В остальных местах — обычная |
| Где маскота нет | Экраны отказа, истечения срока и блокировки. Там тон сухой, и милый персонаж читается как насмешка |
| Количество | Один маскот на экран. Минимальный размер 64 px |
| Мини-логотип | Индикатор, фавикон, иконка приложения, водяной знак. Как подпись бренда в шапке не используется |
| Порядок в lockup | Сначала появляется логотип, затем включаются огни |
| Снижение движения | При prefers-reduced-motion всё статично: логотип виден сразу, маскот не парит, знак-индикатор просто приглушён |
<!-- связка: знак и огни -->
<div class="c6LogoLockup">
<svg class="c6LogoSvg c6LogoFull c6LogoSizeSm c6LogoRise" viewBox="0 0 1057 217" role="img" aria-label="code6">
<path class="c6LogoInk" d="..."/>
<path class="c6LogoLead" d="..."/>
<path class="c6LogoTrail" d="..."/>
</svg>
<div class="c6LightBar" role="presentation" aria-hidden="true">
<span class="c6LightBarSegment"></span>
<span class="c6LightBarSegment"></span>
</div>
</div>
<!-- знак как индикатор: SVG встроен, поэтому красится токенами -->
<span class="c6MarkLoader c6MarkLoaderMd" role="presentation" aria-hidden="true">
<svg class="c6LogoSvg c6LogoMark" viewBox="0 0 208 217">
<path class="c6LogoLead" d="..."/>
<path class="c6LogoTrail" d="..."/>
</svg>
</span>
<!-- пустое состояние -->
<div class="c6EmptyState">
<img class="c6Mascot c6MascotSm c6MascotIdle" src="/code6_codic_glasses.png" alt="">
<p>Пока ничего нет</p>
</div>
Загрузка
Динамического содержимого у нас больше, чем статического: счётчик мест опрашивается раз в минуту, позиция в очереди обновляется на глазах у кандидата, таблицы вызовов в TERMINAL живут постоянно. Поэтому состояние загрузки — не исключение, а один из штатных видов блока, и выглядеть оно должно так же продуманно, как готовый экран.
Скелет карточки
Геометрия скелета обязана совпадать с геометрией готового блока. Если после подстановки контента страница дёрнулась — скелет сделан неправильно, и лучше бы там стоял спиннер.
Скелет строки таблицы
В плотных таблицах скелет ставится внутрь настоящих ячеек — тогда ширины колонок держит сама таблица и после загрузки ничего не перестраивается.
| Код | Департамент | Статус | Время |
|---|---|---|---|
Спиннер
Три размера. Кольцо берёт цвет границы, дуга — currentColor, поэтому спиннер одинаково работает на карточке, в кнопке и на тёмной плашке без переопределений.
Кнопка
Подпись не удаляется, а скрывается — ширина кнопки не меняется, соседние элементы не сдвигаются, повторное нажатие блокируется через pointer-events.
Индикатор страницы
Тонкая полоса под шапкой на время перехода или тяжёлого запроса. В отличие от бегущего отблеска с огнями, здесь только синий: это процесс, а не брендовый момент.
Изображение
Рамка держит размер и показывает подложку скелета, изображение появляется по load с плавным проявлением. Ни одного сдвига раскладки.
<div class="c6MediaFrame" style="aspect-ratio:16/9">
<img class="c6MediaImage" src="..." alt="">
</div>
// Класс добавляется по событию загрузки, а не по таймеру:
// иначе на медленной сети картинка проявится раньше, чем придёт.
function bindMediaFadeIn(imageElement) {
if (imageElement.complete) {
imageElement.classList.add('c6MediaImageLoaded');
return;
}
imageElement.addEventListener('load', () => {
imageElement.classList.add('c6MediaImageLoaded');
}, { once: true });
}
Живые значения
Числа, которые обновляются сами: свободные места, позиция в очереди, количество проверенных за сутки заявок, счётчик людей на смене. Их нельзя заменять скелетом при каждом опросе — вместо этого значение подсвечивается ровно в момент изменения.
Попробуйте
Свободно мест: 56
Устаревшие данные
Если опрос не прошёл, значение не прячется и не обнуляется. Блок приглушается, рядом появляется отметка времени последнего успешного обновления. Показать старое значение честно лучше, чем показать его уверенно.
56 мест
Обновляется каждую минуту
Данные от 21:04, связь потеряна
Обновление на месте
function updateLiveValue(valueElement, nextValue) {
const nextText = String(nextValue);
// Ничего не мигаем, если значение не изменилось:
// подсветка без изменения читается как сбой.
if (valueElement.textContent === nextText) {
return;
}
valueElement.textContent = nextText;
// Снять и вернуть класс мало — браузер не перезапустит анимацию,
// пока не произойдёт пересчёт раскладки. Обращение к offsetWidth
// и есть этот принудительный пересчёт.
valueElement.classList.remove('c6ValueChanged');
void valueElement.offsetWidth;
valueElement.classList.add('c6ValueChanged');
}
Правила загрузки
| Правило | Значение |
|---|---|
| Порог показа | Индикатор не появляется первые 200 мс. Мелькание раздражает сильнее, чем ожидание |
| Минимальная жизнь | Показанный индикатор держится не меньше 400 мс, иначе он мигнёт и исчезнет |
| Совпадение геометрии | Скелет повторяет размеры готового содержимого. Сдвиг раскладки после загрузки — ошибка |
| Ширины скелета | Модификаторами c6SkeletonWide*, не атрибутом style |
| Пусто и загружается | Разные состояния. «Ничего не найдено» во время загрузки не показывается никогда |
| Повторный опрос | Не заменяет содержимое скелетом. Меняется только само значение |
| Ошибка опроса | Старое значение остаётся, блок приглушается, появляется отметка времени |
| Огни против спиннера | Бегущий отблеск с огнями — брендовый момент: загрузка страницы, заставка. Обычная загрузка данных — синий спиннер или скелет |
| Снижение движения | При prefers-reduced-motion все шиммеры и вращения останавливаются, подложки остаются |
| Индикатор навигации | Запускается по факту перехода, а не по клику: отменённый клик, модальное окно и переключение схемы его не включают |
| Повторный запуск | Каждый новый переход перезапускает анимацию с нулевого кадра. Повторное добавление класса само по себе её не перезапускает — нужен принудительный пересчёт раскладки |
| Остановка | Гасит индикатор только последний запрос. Ранний ответ не должен снимать индикатор более позднего перехода |
Порог и минимальная жизнь индикатора
async function runWithLoader(task, loaderElement, options = {}) {
const minimumDelay = options.minimumDelay || 200;
const minimumVisible = options.minimumVisible || 400;
let shownAt = 0;
const showTimer = setTimeout(() => {
loaderElement.hidden = false;
shownAt = Date.now();
}, minimumDelay);
try {
return await task;
} finally {
clearTimeout(showTimer);
// Если индикатор успел появиться, он обязан прожить минимум,
// иначе пользователь увидит вспышку вместо загрузки.
if (shownAt) {
const visibleFor = Date.now() - shownAt;
if (visibleFor < minimumVisible) {
await new Promise(resolve => setTimeout(resolve, minimumVisible - visibleFor));
}
}
loaderElement.hidden = true;
}
}
Сгенерированный блок
Контейнер для всего, что написала модель: ответы ассистента, автоматические сводки, предварительная оценка заявок, реплики NPC, подсказки в игре. У блока три обязательных признака — штриховая кромка, маркер происхождения и подпись с моделью и временем.
Готовый ответ
По номеру 7KQ2MX4B1P найдено три записи: остановка на трассе 12 сентября, протокол о превышении 14 сентября, вызов эвакуатора 15 сентября.
Совпадений по розыску нет.
Сводка составлена автоматически и может содержать ошибки. Решение принимает человек.
Во время генерации
По номеру 7KQ2MX4B1P найдено три записи: остановка на трассе 12 сентября
До первого токена
Разметка
<article class="c6Generated c6GeneratedStreaming">
<div class="c6GeneratedHead">
<span class="c6GeneratedMark">
<i data-lucide="sparkles" width="13" height="13" aria-hidden="true"></i>Генерируется
</span>
<span class="c6GeneratedMeta">terminal-assist · 16 сент, 21:04</span>
</div>
<div class="c6GeneratedBody">
<p>Текст ответа<span class="c6GeneratedCaret" aria-hidden="true"></span></p>
</div>
<p class="c6GeneratedFoot">Сводка составлена автоматически и может содержать ошибки.</p>
</article>
// Класс состояния снимается, когда поток закончился.
// Кроме анимации он ничего не меняет, поэтому блок
// не «прыгает» в момент завершения.
function finishGeneratedBlock(blockElement, modelName, finishedAt) {
blockElement.classList.remove('c6GeneratedStreaming');
blockElement.querySelector('.c6GeneratedMark').lastChild.textContent = 'Сгенерировано';
blockElement.querySelector('.c6GeneratedMeta').textContent = modelName + ' · ' + finishedAt;
const caret = blockElement.querySelector('.c6GeneratedCaret');
if (caret) {
caret.remove();
}
}
Правила разметки сгенерированного
| Правило | Значение |
|---|---|
| Обязательные признаки | Штриховая кромка, маркер, подпись с моделью и временем. Ни один не опускается |
| Никогда не как человеческое | Сгенерированный текст не оформляется так же, как написанный участником или администрацией |
| Цвет | Не берёт ни один статусный оттенок. Синий допустим только в индикаторе процесса |
| Анимация | Только во время генерации. После завершения блок статичен |
| Смена состояния | Снятие класса не меняет геометрию: блок не смещается в момент завершения |
| Оговорка | Там, где на основе текста принимают решение, внизу стоит строка об автоматическом происхождении |
| Вложенность | Блок не вкладывается в другой такой же. Несколько ответов — несколько блоков подряд |
Где применяется
- Ответы ассистента в TERMINAL и в базе знаний.
- Автоматическая сводка по заявке в интерфейсе проверяющего.
- Реплики и подсказки NPC, вынесенные в интерфейс.
- Черновики документов и протоколов до правки человеком.
Правила кода
Именование и читаемость
| Правило | Значение |
|---|---|
| Регистр | camelCase для переменных, функций, ключей объектов, CSS-токенов и классов |
| Имена | Полные слова. departmentIndex, а не di. currentScreen, а не s |
| Сокращения | Только общепринятые: id, url, css |
| Комментарии | Там, где логика неочевидна: почему так, а не что делает строка |
| Обфускация | В исходниках не применяется. Минификация — задача сборки |
| Функции | Одна функция — одна ответственность. Сборка разметки отдельно от логики состояния |
function renderScreen(screenName) {
const screen = screens[screenName];
// Класс режима нужен вёрстке: от него зависит,
// какие соседние блоки встают в пару на десктопе.
pageElement.className = 'page mode-' + screen.mode;
pageElement.innerHTML = screen.blocks
.map(blockName => blockRenderers[blockName](screen.data, screen))
.join('');
bindInteractiveControls();
refreshIcons();
}
CSS
| Правило | Значение |
|---|---|
| Инлайн-стили | Не применяются. Ни атрибутом style, ни строкой стиля из JS |
| Динамические значения | Через класс-модификатор или CSS-переменную на элементе |
| Группировка | Файл организован по токенам: объявление, затем всё, что его читает. Блок компонента начинается с перечня токенов, которые он использует |
| Адаптивы | Медиазапрос компонента лежит рядом с компонентом, а не в общей секции в конце файла |
| Хардкод цвета | Не применяется. Любой цвет — только токен |
| Утилиты против CSS | Сначала утилита Bootstrap в разметке, свой класс — если утилиты не хватает |
/* --- прогресс -----------------------------------------------------------
Токены: --c6TrackEmpty, --c6ActionFill, --c6StatusWaiting
------------------------------------------------------------------------ */
.c6ProgressTrack { height: 8px; border-radius: 5px; background: var(--c6TrackEmpty); overflow: hidden; }
.c6ProgressFill { display: block; height: 100%; border-radius: 5px; }
.c6ProgressFillAction { background: var(--c6ActionFill); }
.c6ProgressFillWaiting { background: var(--c6StatusWaiting); }
@media (min-width: 900px) {
.c6ProgressTrack { height: 10px; }
}
Тон и раскрытие информации
Часть системы, а не отдельная редполитика: от неё зависит, какие элементы вообще существуют. Подробная инструкция обучает кандидата, а обученный кандидат проходит отбор, не будучи подходящим.
| Показываем | Не показываем |
|---|---|
| Статусы, даты, коды, сроки | Причины отказа и комментарии проверяющих |
| Позицию в очереди и скорость обработки | Обещания сроков проверки |
| Требования и стоп-факторы | Советы по подготовке и разбор ошибок |
| Названия шагов отбора | Внутренние процедуры, обучение, наставничество |
| Характер департаментов | Устройство служб изнутри |
Сдержанность касается раскрытия информации, а не интонации. «В этот раз не получилось» — нормально. «Вот что стоит подтянуть» — нет. Тему закрывает формулировка из правил сообщества: «Решения по заявкам не комментируются».