Быстрый старт

Пять шагов от пустой директории до цветной кнопки на экране. Всё, что глубже, — на соседних страницах.

Самая короткая полезная страница сайта. Здесь нет вариантов, оговорок и «а если у вас Nuxt» — один путь, который доводит до работающего компонента. Разветвления начинаются на установке и в конфигурации.

Что понадобится

  1. Node 22 или новее. Ниже пакет не ставится: он ESM-only и объявляет это в engines.
  2. "type": "module" в package.json приложения. CommonJS не поддерживается.
  3. Vue 3.5 или новее и сборка на Vite. Другие сборщики не запрещены, но и не проверяются.

Установка

npm i @feugene/granularity vue @floating-ui/dom @unocss/reset
pnpm add @feugene/granularity vue @floating-ui/dom @unocss/reset
yarn add @feugene/granularity vue @floating-ui/dom @unocss/reset
bun add @feugene/granularity vue @floating-ui/dom @unocss/reset

И то, что работает только на сборке и в бандл не попадает:

bash
yarn add -D unocss @feugene/unocss-preset-granular @unocss/preset-mini

@floating-ui/dom — обязательная peer-зависимость, а не рекомендация. На ней держится позиционирование GrSelect, GrDropdown, GrAutocomplete, GrTreeSelect, GrTooltip и GrPopover. Без неё приложение упадёт на первом же импорте выпадающего списка. Модальный слой, наоборот, не требует ничего: ловушка фокуса, inert и порядок Esc — собственные примитивы пакета.

Конфигурация

CSS дизайн-системы не импортируется файлами — его собирает UnoCSS из granular-провайдера, а провайдер читает собранный dist пакета. Поэтому точка правды одна:

uno.config.ts
import { defineConfig, presetMini } from 'unocss'
import { granularContent, presetGranularNode } from '@feugene/unocss-preset-granular/node'
import granularityProvider from '@feugene/granularity/granular-provider/node'

// Один объект на оба вызова. Дадите им разойтись — компоненты приедут без стилей.
const granular = {
  providers: [granularityProvider],
  components: [{ provider: '@feugene/granularity', names: ['GrButton'] }],
  themes: { names: ['light', 'dark'] },
  layer: 'granular' as const,
}

export default defineConfig({
  // `@unocss/vite` читает `content` только из верхнего уровня конфига,
  // а не из пресета. Пропустите — extractor не заглянет в `dist` пакета.
  content: granularContent(granular),
  presets: [presetMini(), presetGranularNode(granular)],
})

components можно не писать вовсе — тогда в CSS приедут все компоненты провайдера. Перечисление сужает лист до нужного набора, и это единственное место, где вы платите за гранулярность вниманием.

vite.config.ts
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'

export default defineConfig({ plugins: [Vue(), UnoCSS()] })

Три импорта, и порядок между ними значим

src/main.ts
import '@unocss/reset/tailwind-compat.css'
import 'virtual:uno:granular.css'
import 'virtual:uno.css'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

Виртуальных модуля два, и оба обязательны. В слое granular лежит фундамент — :root { --gr-* }, базовый слой, темы, покомпонентные переменные. Утилитарные классы, которыми нарисованы шаблоны компонентов, генерируются в общий virtual:uno.css. Забыть второй импорт — получить компоненты с токенами, но без раскладки: цвета есть, вёрстки нет.

Без layer: 'granular' отдельного модуля слоя не будет и всё приедет одним virtual:uno.css — так тоже правильно. Слой нужен, когда важен порядок относительно собственного CSS приложения.

Первый компонент

src/App.vue
<script setup lang="ts">
import { GrButton } from '@feugene/granularity/components/GrButton'
</script>

<template>
  <GrButton tone="primary">Save</GrButton>
</template>

Импорт из подпути, а не из корня пакета, — это и есть гранулярность. Он тянет один компонент и его граф, а не барель.

Запускайте vite. Кнопка должна быть цветной, с тенью и с фокусным кольцом по Tab. Если она серая или голая — почти наверняка дело в одном из трёх импортов CSS; разбор ровно этого случая есть на странице типовых поломок.

Дальше

Последняя ревизия: 2026-09-01