Типовые поломки
Симптом, причина, починка. Собрано по тем случаям, которые ломались на самом деле, а не по воображаемым.
На этой странице
- Компонент серый и голый
- Рамки пропали, кнопки бесцветные, заголовки одного кегля
- Стили пропали у одного конкретного компонента
- Полупрозрачный фон приехал глухим
- Сборка падает на импорте из пакета
- Сборка падает с ComponentRootImportError
- Сборка падает с InvalidConfigError
- Место под иконку есть, иконки нет
- TypeScript не находит типы подпути
- Авто-импорт не срабатывает
- Тёмная тема мигает при загрузке
- Панель оверлея прилипла не туда
- В тестах оверлеи не находятся
- Ничего из перечисленного
Почти все поломки на входе — это одно из трёх: не хватает импорта CSS, сброс объявлен не в том слое, или в проекте нет обязательной peer-зависимости. Ниже — по симптому.
Компонент серый и голый
Что видно. Кнопка без цвета, без скруглений, без отступов — как будто CSS нет вовсе.
Почему. Не импортирован лист. Токены, базовый слой, темы, стили компонентов и утилиты приезжают одним виртуальным модулем, и импортирует его приложение, а не интеграция:
import './reset.css'
import 'virtual:granum.css'Рамки пропали, кнопки бесцветные, заголовки одного кегля
Что видно. Похоже на предыдущее, но CSS явно есть: цвета текста и фона на месте, а всё, что рисует библиотека, — нет.
Почему. Сброс объявлен после слоёв granum и потому выигрывает у всей
библиотеки. Нелейерный CSS бьёт ЛЮБОЙ @layer независимо от специфичности, а
слой, объявленный позже, бьёт объявленный раньше. * { border-width: 0 } из
сброса снимает рамки, h1…h6 { font-size: inherit } — кегль заголовков, а
button { background-color: transparent } из @unocss/reset/tailwind.css —
ещё и фон кнопок. В tailwind-compat.css эта строка закомментирована как раз
ради библиотек компонентов.
@import '@unocss/reset/tailwind-compat.css' layer(reset);Файл обязан импортироваться первым: место слоя в каскаде задаёт его первое появление. Ни ошибки, ни предупреждения перепутанный порядок не даёт.
Если сброс лежит в <style> внутри шаблона страницы, порядок ссылок может
оказаться не тем, на который вы рассчитываете: сборщик вставляет такой блок
после <link> на лист. Тогда порядок задают объявлением — отдельной строкой
@layer reset; в файле, который импортируется раньше всех.
Стили пропали у одного конкретного компонента
Что видно. Остальные в порядке, а новый добавленный — голый.
Почему. Компонента нет в списке components. Добавьте — или поставьте
'all', тогда в лист приедут все.
По умолчанию это не молчит: js.guard роняет сборку на импорте компонента вне
выборки. Если вы поставили 'warn' или 'off', симптом вернётся к прежнему —
голый компонент без единого сообщения.
Полупрозрачный фон приехал глухим
Что видно. Подложка оверлея непрозрачна, bg-[var(--gr-bg)]/55 заливает
сплошным цветом.
Почему. Выключен extraRules у движка. preset-wind3 не умеет применять
альфу к произвольному цвету в скобках: он отдаёт background-color: var(--gr-bg), молча теряя альфу — класс совпал, CSS неверный, ошибки нет.
Движок добирает это одним правилом поверх пресета, и оно включено по умолчанию.
Сборка при этом молчит, а granum doctor покажет provider-dialect-mismatch:
пакет собран под диалект unocss/preset-wind3+granum@66, а движок без
доп-правил говорит на unocss/preset-wind3@66 — это другой словарь, хотя имена
классов те же. Диалекты описаны в документации granum
(ru,
en).
Раньше таких правил было восемь семейств — sr-only, animate-spin,
divide-*, space-*, object-* и прочее, — потому что дыры затыкались в
preset-mini. В словаре preset-wind3 семь из восьми родные, и добирать
осталось только альфу.
Сборка падает на импорте из пакета
Что видно.
Failed to resolve import "@floating-ui/dom"Почему. Библиотека не установлена. Это обязательная peer-зависимость: на ней
держится позиционирование всех плавающих панелей — GrSelect, GrDropdown,
GrDropdownMenu, GrContextMenu, GrAutocomplete, GrTreeSelect,
GrTooltip, GrPopover — и всего, что их содержит: GrDataTable,
GrPagination, GrColorPicker, GrSidebar. Импорт из корня пакета тянет её
всегда. Это ошибка сборки, а не рантайма: так её показывает dev-сервер, а в
vite build она звучит как Rolldown failed to resolve import "@floating-ui/dom".
Пакет держит библиотеку внешней намеренно — иначе приложение, которое само
использует floating-ui, получило бы вторую копию.
yarn add @floating-ui/domСборка падает с ComponentRootImportError
Что видно.
ComponentRootImportError: Components 'GrButton', 'GrCard' are imported from the
package root '@feugene/granularity' in 'src/App.vue'. Import components from
'@feugene/granularity/components/GrButton', … or from 'virtual:granum/components'Почему. Компонент импортирован из корня пакета:
import { GrButton } from '@feugene/granularity'. Селекция granum видит компонент
только по подпути, а корень тянет весь пакет — в dev и в SSR это все его
компоненты. С granum 2 это ошибка guard, а не тихая поломка. Утилиты, плагины,
константы и типы из корня брать можно — запрет касается только компонентов.
import { GrButton } from '@feugene/granularity/components/GrButton'Или уберите импорт вовсе и поставьте авто-импорт: резолвер сам вставит подпуть.
Сборка падает с InvalidConfigError
Что видно.
Invalid granum config at 'appSource': unknown option — did you mean 'appSources'?Почему. granum 2 проверяет granum.config строго: опечатка в ключе, ключ
пресета v1 (themes.themeFiles, scan) и токен с префиксом -- в
tokenOverrides или define — ошибка с путём до поля, а не молча проигнорированная
настройка. Исправьте поле, которое назвала ошибка: для опечатки она подскажет
ближайший ключ, для ключа v1 — куда он делся.
Место под иконку есть, иконки нет
Что видно. Пустой квадрат там, где ожидалась иконка, переданная классом
(icon="i-lucide-user").
Почему. Класс i-lucide-* — утилита, и генерирует её ваш конфиг, а не
пакет. Правил иконок в словаре preset-wind3 нет: без правила класс остаётся
классом, место под иконку есть, картинки нет, и сборка проходит молча.
Правило отдают фабрике движка — windEngine({ rules: [iconRule] }) — или
привозят провайдером. Разница описана на странице
установки: правило от провайдера не меняет отпечаток
словаря приложения, а правило из конфига меняет, и granum пересчитывает классы
всех пакетов заново. На одном-двух провайдерах это незаметно.
Альтернатива — передать иконку Vue-компонентом, тогда настраивать нечего.
Собственные иконки пакета — стрелка селекта, крестик очистки, галочка, спиннер —
вкомпилированы в dist и от вашего конфига не зависят.
TypeScript не находит типы подпути
Что видно.
Cannot find module '@feugene/granularity/components/GrButton'
or its corresponding type declarations. ts(2307)При этом импорт из корня пакета типизируется нормально — но компонент из корня granum отвергает ошибкой сборки, так что это не выход.
Почему. "moduleResolution": "node" в tsconfig.json. Старый алгоритм не
читает поле exports, а типы подпутей объявлены только там. Нужен "bundler"
или "node16".
Авто-импорт не срабатывает
Что видно. <GrButton> в шаблоне превращается в неизвестный элемент, Vue
ругается в консоли.
Почему. Одно из трёх:
- резолвер не подключён к
unplugin-vue-components— проверьте массивresolvers, а не только сам плагин; - компонент приезжает из спутника, которого нет в
package.jsonприложения: резолвер берёт пакеты оттуда, а компоненты — из ихexports. Имя, которого ни один пакет не экспортирует (опечатка, свой компонент), он не резолвит; - у
GranularityResolverзадан свойprefixилиexclude, и имя под него не подходит. Регистр значения не имеет:<gr-button>плагин сам приводит кGrButton.
Тёмная тема мигает при загрузке
Что видно. На долю секунды светлый фон, потом тёмный.
Почему. Сервер (или статический HTML) не знает выбор пользователя, а атрибут
темы ставится уже после гидрации. Лечится инлайновым скриптом в <head> до
первой отрисовки — готовый код на странице SSR.
Панель оверлея прилипла не туда
Что видно. Выпадающая панель или модалка позиционируется относительно контейнера, а не окна; при прокрутке уезжает.
Почему. У контейнера, названного в portalTarget, есть transform,
filter, contain, perspective или will-change. Любое из них создаёт
containing block для position: fixed, и панель начинает считать позицию от
контейнера. Требование к контейнеру портала жёсткое и звучит именно так: ни
одного из этих свойств.
В тестах оверлеи не находятся
Что видно. Тест монтирует компонент, открывает панель — и не находит её в документе. Ошибок в консоли нет.
Почему. Между тестами не вызывается resetGranularityDom. Корень портала и
хост живого региона кэшированы модулями: после document.body.innerHTML = ''
они указывают на узлы вне документа, и следующий монтаж телепортирует оверлеи
туда. Подробности — на странице тестирования.
Ничего из перечисленного
Если симптом не отсюда — заведите issue в репозитории. Случай, который пришлось разбирать дважды, попадает на эту страницу.