Troubleshooting
Symptom, cause, fix. Collected from the cases that actually broke rather than from imaginary ones.
Machine-translated, not yet reviewed. Read the original
Almost every breakage at the entrance is one of three things: a missing CSS import, a preset config that disagrees with itself, or a missing required peer dependency in the project. Below, by symptom.
The component is grey and bare
What you see. A button with no colour, no radius, no padding — as if there were no CSS at all.
Why. The foundation is not wired up: the tokens, the base layer and the themes arrive through the layer’s virtual module. Check that the entry point has both modules, in this order:
import '@unocss/reset/tailwind-compat.css'
import 'virtual:uno:granular.css' // tokens, base, themes
import 'virtual:uno.css' // utility classesIf layer: 'granular' is not set in the preset options, there is no separate
layer module and everything arrives in a single virtual:uno.css — then the
second line is enough.
The colours are there, the layout is not
What you see. The component is coloured, but the elements are stacked in a column, the spacing has slipped and the icon overlaps the text.
Why. virtual:uno.css was forgotten. The utility classes the component
templates are drawn with are generated into that one, while the granular layer
carries the foundation only.
The styles are gone for one particular component
What you see. The rest are fine, and the newly added one is bare.
Why. One of two things:
- The component is missing from the preset’s
componentslist. Add it — or drop the list entirely, and then everything reaches the sheet. granularContent(options)andpresetGranularNode(options)were given different option objects. The extractor then scans something other than what the preset generates. Keep one object for both calls.
The spinner does not spin, hidden text is visible
What you see. animate-spin does nothing, dividers are not drawn, and
service labels — a dialog heading, a table caption — are shown to the user as
ordinary text.
Why. includeExtraRules is off. The components use utilities that
presetMini does not have at all — sr-only, animate-spin, divide-*,
space-*, backdrop-* — and the preset pulls them from a separate package. That
is a condition of the thing working rather than a decoration: with
includeExtraRules: false the class stays in the markup, the CSS never appears,
and the build passes in silence.
The application throws on a dropdown import
What you see.
Failed to resolve import "@floating-ui/dom"Why. The library is not installed. It is a required peer dependency: it is
what positions GrSelect, GrDropdown, GrAutocomplete, GrTreeSelect,
GrTooltip and GrPopover. The package keeps it external deliberately —
otherwise an application that already uses floating-ui would end up with a second
copy.
yarn add @floating-ui/domThere is space for the icon but no icon
What you see. An empty square where an icon passed as a class was expected
(icon="i-lucide-user").
Why. The class i-lucide-* is a UnoCSS utility, and your config generates it,
not the package. You need presetIcons and an icon collection; without them the
class stays a class and the build passes in silence. The alternative is to pass
the icon as a Vue component, and then there is nothing to configure.
The package’s own icons — the select arrow, the clear cross, the check mark, the
spinner — are compiled into dist and do not depend on your config.
TypeScript cannot find the subpath types
What you see.
Cannot find module '@feugene/granularity/components/GrButton'
or its corresponding type declarations. ts(2307)Meanwhile an import from the package root types fine and the build passes.
Why. "moduleResolution": "node" in tsconfig.json. The old algorithm does
not read the exports field, and the subpath types are declared only there. You
need "bundler" or "node16".
Auto-import does not fire
What you see. <GrButton> in a template turns into an unknown element and Vue
complains in the console.
Why. One of three things:
- the resolver is not wired into
unplugin-vue-components— check theresolversarray, not just the plugin itself; - the component comes from a companion package whose resolver is registered
after the core one. The core resolver is greedy: it claims any name
starting with
Gr. Companion resolvers work from an explicit list and therefore go first; - the name is written in kebab case where the resolver expects Pascal. The resolver recognises components by their prefix, and the prefix has to match.
The dark theme flashes on load
What you see. A light background for a fraction of a second, then a dark one.
Why. The server (or the static HTML) does not know the user’s choice, and the
theme attribute is set only after hydration. The cure is an inline script in
<head> before the first paint — the ready-made code is on the
SSR page.
An overlay panel is stuck in the wrong place
What you see. A dropdown panel or a modal positions itself relative to a container rather than to the window, and drifts on scroll.
Why. The container named in portalTarget has a transform, filter,
contain, perspective or will-change. Any of them creates a containing block
for position: fixed, and the panel starts measuring from the container. The
requirement for a portal container is strict and reads exactly like that: not one
of those properties.
Overlays are not found in tests
What you see. The test mounts a component, opens a panel — and does not find it in the document. No errors in the console.
Why. resetGranularityDom is not called between tests. The portal root and
the live region host are cached by their modules: after
document.body.innerHTML = '' they point at nodes outside the document, and the
next mount teleports the overlays there. The details are on the
testing page.
None of the above
If the symptom is not here, write to the repository discussions. A case that had to be worked through twice ends up on this page.