Единый язык интерфейса
Всё, из чего собирается приложение. Любой новый экран берёт элементы отсюда — тогда интерфейс остаётся одним целым. Системные контролы браузера не используются нигде: они выглядят по-разному в каждой ОС и ломают вид.
Цвет
Токены объявлены в :root и .dark, меняются вместе с темой.
Поверхности
--bg--bg-sidebar--bg-modal--bg-raised--bg-sunkenТекст
--ink--ink-secondary--ink-faintЛинии и подсветка
--line--line-strong--hov--hov-strongАкценты
--accent--accent-hover--accent-soft--dangerТипографика
Восемь ступеней и ни одной мимо. Размер задаётся классом шкалы, а не числом по месту.
text-display40название страницыtext-head30заголовок экранаtext-title20заголовок разделаtext-lead15.5заголовок блока, название окнаtext-body14основной текст, меню, строкиtext-small12.5пояснения, подписи разделовtext-micro11.5счётчики, kbd, подписи осейtext-nano10цифры и инициалы внутри чипов 16pxГлубина и форма
Четыре радиуса и пять уровней тени.
--r-sm--r-md--r-lg--r-xl--shadow-xs--shadow-sm--shadow-md--shadow-lg--dur-fast · 120 мс — наведение--dur · 180 мс — переключение--dur-slow · 260 мс — появление--ease-out · выезд и появлениеМатериал
Стекло — материал плавающего слоя: меню, поповеры, диалоги, шторки, доки, панели инструментов. На содержимое страницы оно не идёт: текст поверх размытия читается хуже.
Преломляющая грань — .nc-spec
Движение
--ease-liquid · появление и переезд--ease-bounce · возврат после нажатия--dur-morph · 320 мс — смена формы--dur-liquid · 420 мс — выезд слоя--press · 0.965 — просадка под пальцемПоля ввода
Переключатели
Ни одного системного input — всё нарисовано нами.
Тумблер
Флажок
Радиокнопки
Выбор
Вместо системных select и календаря — свои меню с галочкой.
Виды
Группы и поиск
Дата
Сегменты
Ползунки
Тянутся мышью, стрелками и Home/End.
Недоступен
Плашки
Тона
Паттерны
Строки настроек
Верх сайдбара: поиск и режимы
Поиск — строкой во всю ширину: самое частое действие не прячется среди иконок. Режимы — сегменты поровну, подпись в подсказке: пилюля с подписью не помещалась в 240px и обрезалась. Счётчик непрочитанного прячется, когда режим открыт.
Полоса вкладок
Вкладка не уже 132px: ниже от названия остаётся многоточие, и выбирать не из чего. Упёрлись в пол — полоса листается вбок (колесом тоже), а не давит вкладки дальше. Кнопки по краям прижаты: «назад/вперёд» слева, «+» справа.
Живые иконки
Иконки из src/components/ui/icons/ оживают от наведения на кнопку: ref отдаётся иконке, hoverProps — кнопке (useIconAnimation). Заменяем только те, чей глиф совпадает с обычным lucide; настройку reduced motion хук уважает сам.
Эффекты
Блик по рамке (BorderBeam) и знак продукта (Logo). Родителю нужны position и скругление.
Метеоры на фоне и маркерная подсветка фразы.
Meteors — детерминированная раскладка, гидрация не расходится. Marker прорисовывается один раз при появлении.
Смена темы — кругом от места клика (setThemeAnimated, View Transitions); без поддержки и при reduced motion переключается мгновенно.
Появление по прокрутке
Reveal ждёт, пока блок попадёт в кадр, и показывает его один раз — мигание при каждом проходе мимо раздражает сильнее, чем радует. Считает наблюдатель, а не обработчик прокрутки. Если документ не отрисовывается (фоновая вкладка, краулер, старый браузер), содержимое показывается сразу: украшение не имеет права оставить страницу пустой.
Меню
Карточка и пустое состояние
Карточка галереи и канбана: подъём под курсором, тень усиливается.
Пока пусто
Записи появятся здесь, как только вы их добавите
Элементы студии приложений
Поле кода и вкладки файлов. Оба класса объявлены вне слоёв: .nc-input перебил бы им и шрифт, и высоту.
Вкладки файлов
Поле кода
Tab внутри поля добавляет два пробела, а не уводит фокус: это редактор, а не форма.
Мобильные элементы
Ниже 768px приложение превращается в список дел: нижние вкладки, строки задач, шторки.
Строка задачи
Флажок круглый и красится от состояния: просроченная задача — цветом опасности, «в работе» — акцентом. Выполненная гаснет и уходит в раздел «Выполнено».
Верхняя полоса разделов
Пространство, текущий раздел и быстрые переходы — чат, календарь, ассистент. Именно те, которых нет в нижних вкладках: дублировать «Входящие» сюда незачем. Пилюля показывает, где вы, и открывает список разделов. Непрочитанное — точкой, без числа: это не счётчик задач, а «там что-то есть».
Нижний док: вкладки и поиск
Панель висит над содержимым, а не прижата к краю: под ней видно, что список продолжается. Подложка активной вкладки одна и переезжает пружиной — переход читается как движение по панели, а не как подмена картинки. Поиск отдельным кругом: это действие, а не раздел. Клавиатура поднята — data-hidden уводит док вниз, иначе он подпрыгивает вместе с ней.
Панель форматирования над клавиатурой
Появляется только когда с телефона действительно пишут. Держится на --kb — настоящей высоте клавиатуры из visualViewport: на iOS она наезжает поверх и в раскладке её не видно. Нажатия идут по onPointerDown с preventDefault, иначе фокус уходит из редактора, клавиатура уезжает и действие применяется не к тому месту.
Дни в «Планах»
Шторка и плавающая кнопка
Рамка окна
Только для Windows-приложения. В браузере эти кнопки скрыты — здесь они показаны принудительно, обёрткой .nc-wincontrols-demo.
Кнопки окна на полосе вкладок
Значки обычные lucide, а не системные глифы Windows: рядом в полосе стоят «назад» и «+», и чужой набор читался бы как вставленный из другой программы. Область нажатия при этом шире рисунка — 40px против 26px — и упирается в самый угол окна: мышь в угол бросают не глядя, и кнопка с отступом от края означала бы, что окно не закрылось. Наведение на «закрыть» подкрашено токеном --danger-soft, а не полной красной плашкой: она рядом с чернильной полосой выглядит чужой.
Правила
- Никаких системных контролов. `input[type=checkbox]`, `input[type=range]` и `select` в коде приложения запрещены — берите
Checkbox,Switch,Slider,Select. - Цвет только токеном. Хардкод вроде `#fff` допустим лишь внутри самих токенов; в компонентах — `var(--ink)`, `bg-paper`, `text-ink-secondary`.
- Базовые стили — в `@layer base`. Правило вне слоёв перебивает утилиты Tailwind.
- Движение по токенам. `--dur-fast` на наведение, `--dur` на переключение, `--dur-slow` на появление, всегда с `--ease-out`.
- У каждой иконки есть имя. `IconButton` требует `label` — он же подсказка, он же доступное имя.
- Размер текста — только классом шкалы.
text-body,text-smallи остальные шесть. Запись вида `text-[13.5px]` запрещена: из таких разовых решений и складывается вид «собрано из утилит». - Акцент — редкий гость. Им красим ссылки, выделение, фокус и отметки. Где ты находишься — активный пункт, режим, вкладка — рисуем нейтральной подложкой. Главные кнопки — чёрные пилюли (
variant="primary"), цветная — одна на экран. - Блок внутри страницы отделяем фоном, а не рамкой.
.nc-panelвместо обведённой карточки: набор прямоугольников в рамках читается как админка. - Всплывающее не имеет права уезжать за экран. Меню, поповер и панель считают место сами: не хватило снизу — открываются вверх, не хватило и там — прижимаются к краю и прокручиваются внутри. Панель с обрезанными пунктами — ошибка, а не мелочь.
- Без капса. Подписи разделов — обычным регистром,
text-smallприглушённым цветом. Капс с растянутым трекингом — почерк бутстрап-шаблонов.