Типовые поломки
Симптом, причина, починка. Собрано по тем случаям, которые ломались на самом деле, а не по воображаемым.
Почти все поломки на входе — это одно из трёх: не хватает импорта CSS, конфиг пресета разошёлся сам с собой, или в проекте нет обязательной peer-зависимости. Ниже — по симптому.
Компонент серый и голый
Что видно. Кнопка без цвета, без скруглений, без отступов — как будто CSS нет вовсе.
Почему. Не подключён фундамент: токены, базовый слой и темы приезжают виртуальным модулем слоя. Проверьте, что в точке входа есть оба модуля и именно в этом порядке:
import '@unocss/reset/tailwind-compat.css'
import 'virtual:uno:granular.css' // токены, base, темы
import 'virtual:uno.css' // утилитарные классыЕсли layer: 'granular' в опциях пресета не задан, отдельного модуля слоя нет и
всё приезжает одним virtual:uno.css — тогда достаточно второй строки.
Цвета есть, вёрстки нет
Что видно. Компонент цветной, но элементы сложены в столбик, отступы съехали, иконка налезает на текст.
Почему. Забыт virtual:uno.css. Утилитарные классы, которыми нарисованы
шаблоны компонентов, генерируются именно в него, а слой granular несёт только
фундамент.
Стили пропали у одного конкретного компонента
Что видно. Остальные в порядке, а новый добавленный — голый.
Почему. Одно из двух:
- Компонента нет в списке
componentsпресета. Добавьте — или уберите список вовсе, тогда в лист приедут все. granularContent(options)иpresetGranularNode(options)получили разные объекты опций. Экстрактор при этом сканирует не то, что генерирует пресет. Держите один объект на оба вызова.
Спиннер не крутится, скрытый текст виден
Что видно. animate-spin не работает, разделители не рисуются, а
служебные подписи — заголовок диалога, подпись таблицы — показываются
пользователю обычным текстом.
Почему. Выключен includeExtraRules. Компоненты пользуются утилитами,
которых в presetMini нет вовсе — sr-only, animate-spin, divide-*,
space-*, backdrop-*, — и пресет добирает их отдельным пакетом. Это условие
работоспособности, а не украшение: с includeExtraRules: false класс остаётся в
разметке, CSS не появляется, и сборка проходит молча.
Приложение падает на импорте выпадающего списка
Что видно.
Failed to resolve import "@floating-ui/dom"Почему. Библиотека не установлена. Это обязательная peer-зависимость: на ней
держится позиционирование GrSelect, GrDropdown, GrAutocomplete,
GrTreeSelect, GrTooltip и GrPopover. Пакет держит её внешней намеренно —
иначе приложение, которое само использует floating-ui, получило бы вторую копию.
yarn add @floating-ui/domМесто под иконку есть, иконки нет
Что видно. Пустой квадрат там, где ожидалась иконка, переданная классом
(icon="i-lucide-user").
Почему. Класс i-lucide-* — утилита UnoCSS, и генерирует её ваш конфиг, а
не пакет. Нужен presetIcons и коллекция иконок; без них класс остаётся
классом, и сборка проходит молча. Альтернатива — передать иконку
Vue-компонентом, тогда настраивать нечего.
Собственные иконки пакета — стрелка селекта, крестик очистки, галочка, спиннер —
вкомпилированы в dist и от вашего конфига не зависят.
TypeScript не находит типы подпути
Что видно.
Cannot find module '@feugene/granularity/components/GrButton'
or its corresponding type declarations. ts(2307)При этом импорт из корня пакета типизируется нормально, и сборка проходит.
Почему. "moduleResolution": "node" в tsconfig.json. Старый алгоритм не
читает поле exports, а типы подпутей объявлены только там. Нужен "bundler"
или "node16".
Авто-импорт не срабатывает
Что видно. <GrButton> в шаблоне превращается в неизвестный элемент, Vue
ругается в консоли.
Почему. Одно из трёх:
- резолвер не подключён к
unplugin-vue-components— проверьте массивresolvers, а не только сам плагин; - компонент приезжает из спутника, а его резолвер зарегистрирован после
ядрового. Резолвер ядра жадный: он забирает любое имя на
Gr. Резолверы спутников работают по явному списку и потому идут первыми; - имя написано в кебаб-кейсе там, где резолвер ждёт паскаль. Резолвер узнаёт компоненты по префиксу, и префикс должен совпасть.
Тёмная тема мигает при загрузке
Что видно. На долю секунды светлый фон, потом тёмный.
Почему. Сервер (или статический HTML) не знает выбор пользователя, а атрибут
темы ставится уже после гидрации. Лечится инлайновым скриптом в <head> до
первой отрисовки — готовый код на странице SSR.
Панель оверлея прилипла не туда
Что видно. Выпадающая панель или модалка позиционируется относительно контейнера, а не окна; при прокрутке уезжает.
Почему. У контейнера, названного в portalTarget, есть transform,
filter, contain, perspective или will-change. Любое из них создаёт
containing block для position: fixed, и панель начинает считать позицию от
контейнера. Требование к контейнеру портала жёсткое и звучит именно так: ни
одного из этих свойств.
В тестах оверлеи не находятся
Что видно. Тест монтирует компонент, открывает панель — и не находит её в документе. Ошибок в консоли нет.
Почему. Между тестами не вызывается resetGranularityDom. Корень портала и
хост живого региона кэшированы модулями: после document.body.innerHTML = ''
они указывают на узлы вне документа, и следующий монтаж телепортирует оверлеи
туда. Подробности — на странице тестирования.
Ничего из перечисленного
Если симптом не отсюда — напишите в обсуждения репозитория. Случай, который пришлось разбирать дважды, попадает на эту страницу.