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

  • Начало работы
  • 5 мин
  • кода: 7

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

Ревизия 2026-10-16

На этой странице

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

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

  1. Node 22 или новее. Так объявлено в engines пакета: Yarn 1 на старой Node откажется его ставить, npm и pnpm только предупредят. Сам пакет ESM-only — "type": "module" и ни одного условия require в exports.
  2. Приложение на ES-модулях. CommonJS-сборки у пакета нет, подключается он через import, как это делает Vite. "type": "module" в package.json приложения рекомендуется, но не проверяется: конфиг granum здесь и так .mjs.
  3. Vue 3.5 или новее и сборка на Vite 8 — под него объявлен peer @feugene/granum. Другие сборщики не запрещены, но и не проверяются.

Установка

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

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

npm i -D vite @vitejs/plugin-vue @feugene/granum @feugene/granum-engine-wind typescript vue-tsc
pnpm add -D vite @vitejs/plugin-vue @feugene/granum @feugene/granum-engine-wind typescript vue-tsc
yarn add -D vite @vitejs/plugin-vue @feugene/granum @feugene/granum-engine-wind typescript vue-tsc
bun add -d vite @vitejs/plugin-vue @feugene/granum @feugene/granum-engine-wind typescript vue-tsc

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

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

CSS дизайн-системы не импортируется файлами — его собирает granum. Состав пакета он читает из манифеста, объявленного экспортом, и по node_modules не ходит: файлы пакета он открывает, только когда манифесту нельзя доверять, и только те, что в манифесте перечислены. Точка правды одна:

granum.config.mjs
import { windEngine } from '@feugene/granum-engine-wind'
import { defineGranumConfig } from '@feugene/granum/vite'

export default defineGranumConfig({
  engine: windEngine(),
  providers: ['@feugene/granularity'],
  components: [{ provider: '@feugene/granularity', names: ['GrButton'] }],
  themes: { names: ['light', 'dark'] },
  appSources: { dirs: ['src'] },
})

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

vite.config.ts
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import { granum } from '@feugene/granum/vite'
import granumConfig from './granum.config.mjs'

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

Конфиг читают в одном месте, а не в двух. У пресета v1 его приходилось отдавать и в content, и в presets одним и тем же объектом: разошлись — компоненты приезжали без стилей, и сборка об этом молчала. Такой развилки больше нет.

Два импорта, и порядок между ними несущий

src/main.ts
import './reset.css'
import 'virtual:granum.css'

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

createApp(App).mount('#app')
src/reset.css
@import '@unocss/reset/tailwind-compat.css' layer(reset);

Сброс уезжает в свой слой, и это не оформление. granum отдаёт всё своё в слои granum.*, а нелейерный CSS бьёт ЛЮБОЙ @layer независимо от специфичности: вне слоя button { color: inherit } из сброса перебил бы утилиты компонента, и кнопка приехала бы с цветом текста страницы.

Слой обязан объявиться раньше слоёв granum — место слоя в каскаде задаёт его первое появление, — поэтому reset.css импортируется первым. Перепутанный порядок не даёт ни ошибки, ни предупреждения.

Виртуальный модуль CSS теперь один — virtual:granum.css. Токены, базовый слой, темы, стили компонентов и утилиты, которыми нарисованы их шаблоны, приезжают одним листом: забыть половину фундамента и получить цвета без вёрстки больше нельзя.

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

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

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

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

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

Дальше