Maia Дизайн-система

Дизайн-система Maia — «стеклянный люкс»

Единственный источник истины по внешнему виду продукта. Кто правит интерфейс — правит сначала здесь, потом в коде.


1. Что это за язык

Полупрозрачные панели поверх живого цветного фона. Глубина строится не рамками и не заливками, а материалом: размытием подложки (backdrop-filter), светлой верхней кромкой и мягкой тенью. Так выглядит visionOS от Apple — отсюда и название направления.

Пять правил, из которых выводится всё остальное:

  1. Фон живёт, панели плавают. Фон — три цветных пятна на градиенте, он зафиксирован (background-attachment: fixed) и не скроллится вместе с содержимым. Панели над ним размывают то, что под ними.
  2. Стекло — материал, а не украшение. Панель, карточка, первичная кнопка, поле ввода — стекло. Разделитель, текст, иконка — нет. Стекло на всём подряд убивает иерархию: если светится каждый элемент, не светится ни один.
  3. Иерархия держится цветом, а не «плоское против стеклянного». Первичная кнопка — изумрудное стекло, вторичная — нейтральное стекло того же материала. Разница в цвете, а не в наличии материала.
  4. Две темы равноправны. Ни одна не «основная». Токены одинаковые, значения разные. Тема ставится атрибутом data-theme на <html>.
  5. Материал отключаем. Пользователь просит систему убрать прозрачность — стекло становится плотной панелью, дизайн не разваливается. Это не «поддержка на будущее», это уже работает.

Почему без оверлея

Вкусовой слой soft из design-ultimate прямо предупреждает: плохо ложится на плотные операционные интерфейсы. Ядро продукта — кабинет агентства, там глубина уходит в точность деталей, а не в пластику. Вместо оверлея действует пол качества impeccable/reference/craft-floor.md.


2. Поверхности

ПоверхностьЧто здесь успехРежимРеализация
Кабинет platform.maia24.ruвыполнить задачу и уйтиoperateReact 18 + Vite + Tailwind 4
Лендинг maia24.ruрешиться и оставить заявкуpersuadeAstro, статика
Виджет на сайте клиентаответ на вопросoperateвстраиваемый скрипт

Режим меняет плотность и типографику, но не палитру и не материал. Лендинг берёт ту же систему и достраивает шкалу вверх (герой), кабинет — вниз (плотные таблицы).


3. Цвет

Полный список значений — в brand/tokens.json. Здесь смысл, а не перечисление.

Бренд

РольТёмная темаСветлая тема
Акцент (--accent)#34D399#0C7E57
Акцент светлее (--accent-2)#6EE7B7#10B981
Знак и первичная кнопка (--mark-grad)#6EE7B7 → #10B981#10C98C → #0BB078

Акцент инвертируется по яркости: на тёмном фоне светлый изумруд, на светлом — глубокий. Поэтому текст на акценте тоже разный: на тёмной теме почти чёрный #04130D, на светлой белый. Это --on-accent, и брать вместо него #fff вслепую нельзя — на светло-зелёной плашке белый текст нечитаем.

Фон

Три радиальных пятна (изумруд, синий, фиолетовый) поверх диагонального градиента. Пятна одинаковые в обеих темах по позиции, разные по светлоте. Резервный плотный цвет — --solid-bg, включается при prefers-reduced-transparency.

Текст

Три ступени: --txt (основной), --txt-2 (вторичный), --txt-3 (третичный).

--txt-3 раньше не проходил контраст ни в одной теме (3.9 и 3.53 при пороге 4.5), а им набраны плейсхолдеры и подписи — то есть текст, а не декор. Прозрачность поднята до .47 в тёмной теме и .60 в светлой: теперь 4.58 и 4.52.

Иерархия при этом сохранилась — три ступени по-прежнему различимы. Но правило остаётся: --txt-3 для того, что можно не прочитать. Всё, что несёт смысл, — минимум --txt-2.

Статусы

Четыре семейства, каждое из четырёх токенов (фон / рамка / текст / точка): ok — работает, tr — пробный период, pa — на паузе, danger — необратимо. Статус всегда несёт цветную точку рядом с текстом: цвет один не должен быть единственным носителем смысла (дальтонизм).


4. Типографика

Inter — и заголовки, и текст. Начертания 400 / 500 / 600 / 700, самохостинг, кириллические подмножества включены.

Google Fonts не подключается. Проект работает под РФ, внешний CDN может отдать таймаут, и страница молча упадёт на системный шрифт — это другой дизайн, о котором никто не узнает.

Шкала

ТокенРазмерГде
--t-cap13pxподписи, метки статусов, шапки таблиц
--t-sm14pxкнопки, поля, навигация, вторичный текст
--t-base16pxосновной текст
--t-lg18pxлид-абзац, имя бренда
--t-xl22pxподзаголовок раздела, h3
--t-2xl30pxзаголовок экрана, h2 на мобильном
--t-3xl42pxh2 на лендинге, h1 на мобильном
--t-4xl56pxгерой лендинга

Кабинет использует --t-cap--t-2xl. Лендинг — всю шкалу.

Правила


5. Ритм и форма

Отступы — одна шкала с шагом 4px: --s1 4 · --s2 8 · --s3 12 · --s4 16 · --s5 20 · --s6 24 · --s7 28 · --s8 32 · --s9 36 · --s10 40 · --s12 56 · --s16 80 · --s20 120.

Значений вне шкалы в коде быть не должно. Исключение одно — оптические поправки в 1–2px, и каждая объясняется комментарием в месте применения.

Радиусы: --radius-xs 9px (внутренние чипы, ползунки) · --radius-sm 11px (поля, пункты меню) · --radius 13px (кнопки, сегменты) · --radius-md 16px (панели, контекстное меню) · --radius-lg 20px (карточки, модальные окна) · --radius-pill 999px (метки статусов).

Радиус растёт вместе с элементом. Маленький радиус на большой карточке читается как обрезанный угол, большой на кнопке — как капсула.

Единственное исключение из шкалы — знак M и логотип клиента: их скругление пропорционально стороне (около 30%) и меняется вместе с размером, поэтому там литерал с комментарием, а не шаг шкалы.

Высота управляющих элементов: 42px в кабинете (плотность), 46px на лендинге (палец на телефоне). Это осознанное расхождение, а не рассинхрон.


6. Материал стекла

Рецепт панели — четыре слоя, все обязательны:

  1. Размытие подложки: backdrop-filter: blur(--glass-blur) saturate(--glass-sat). Насыщенность выше 100% — иначе размытый фон сереет.
  2. Полупрозрачная заливка --panel-bg и рамка --panel-border.
  3. Светлая верхняя кромка внутренней тенью: inset 0 1px 0 --panel-top. Это она даёт ощущение толщины стекла.
  4. Блик — псевдоэлемент ::after с радиальным градиентом из правого верхнего угла. Имитирует источник света, один и тот же для всей страницы.

Плюс внешняя тень 0 20px 50px — со смещением и мягким размытием. Цветное свечение без смещения — это декорация, а не глубина.

Облегчённый вариант .glass-lite — размытие 16px, без блика и внешней тени. Для вложенных элементов: поля ввода, вторичные кнопки, ячейки.

Где стекло запрещено: контекстное меню и выпадающие списки. Меню всплывает поверх произвольного содержимого, и размытие делает его нечитаемым. Там --solid-panel — плотная заливка.


7. Движение

Одна кривая на весь проект: --ease: cubic-bezier(.22,1,.36,1) — резкий старт, долгий выход. Две длительности: --dur .2s (цвет, фон) и --dur-slow .28s (сдвиг, тень).

Что анимируется: подъём кнопки на 1px при наведении, скольжение индикатора в переключателе тем и сегментном фильтре, появление контекстного меню (.12s), проявление блоков лендинга при скролле.

Что не анимируется: смена экрана, появление данных в таблице, переключение темы. Кабинет открывают ежедневно — анимация, милая на первом запуске, на сотом становится задержкой.

При prefers-reduced-motion всё сокращается до .01ms. Не отключается совсем — нулевая длительность ломает некоторые переходы состояний.


8. Иконки

Контурные SVG, сетка 24×24, viewBox="0 0 24 24", скругления на концах линий. Толщина линии — 1.6.

Эмодзи вместо иконки не используются никогда: они рендерятся системным шрифтом, то есть выглядят по-разному на Windows, Mac и Android, и не подчиняются цвету темы.

Размеры в интерфейсе: 20px в навигации, 18px в кнопках и полях, 17px в контекстном меню.


9. Доступность

Контраст: было и стало

Четыре провала найдены замером и закрыты 3 августа 2026.

МестоБылоСталоКак
--txt-3, тёмная3.904.58прозрачность .42 → .47
--txt-3, светлая3.534.52прозрачность .52 → .60
Белый текст на первичной кнопке3.545.49стопы градиента затемнены
Белый текст на опасной кнопке~3.05.53то же
--danger, светлая4.404.99#C4443E#B93B35

Худший случай для кнопок посчитан честно: белый текст на верхнем, самом светлом стопе градиента, поверх самой светлой подложки. Тень под текстом в расчёт не берётся — она не контраст.


10. Расхождения кабинета и лендинга

Лендинг портирован из кабинета. Двенадцать мест разошлись; все сведены 3 августа 2026.

ЧтоБыло в кабинетеБыло на лендингеСтало
--t-base15px16px16px везде, таблицы на --t-table 15px
Шкала отступовs1…s10s1…s20 с пропускамиполная сплошная в обоих
Токены радиусовнет, литералычастичнополная лестница xs…pill в обоих
Высота кнопки42px46pxосознанно оставлено: плотность против пальца
.pillметка статусаакцентная плашкана лендинге переименовано в .tag
--divider тёмная.07.09.09
Статусные цветаестьнетдобавлены на лендинг
--card-solid, --accent-2нетестьдобавлены в кабинет
Inter 700не загружалсязагружалсязагружается в обоих
Толщина линии иконок1.5 – 21.8 – 2.51.6 везде, 30 иконок
Полоса прокруткистекляннаясистемная плоскаястеклянная в обоих
Нажатая кнопкаtranslateY(1px)scale(.975)scale(.975) в обоих

Заодно снялось то, что вскрылось по дороге: цвет первичной кнопки был вписан литералом прямо в правило .btn мимо токена --btn-grad, и правка цвета из одного места не работала. Теперь кнопка красится токеном.


11. Как этим пользоваться

  1. Новый экран или блок — открыть brand/brandbook.html, взять готовый компонент. Своего не изобретать.
  2. Значение цвета, отступа, радиуса берётся токеном. Литерал в коде — ошибка.
  3. Компонент, которого нет в брендбуке, сначала добавляется сюда, потом в код.
  4. Правка токена — правится brand/tokens.json, затем оба CSS-файла. Расхождение между ними означает, что продукт распался на два.