# Миграция

> Переезды между мажорами: что сломали переходы на 1.0, 2.0 и 3.0 и как их пройти.

- Page: https://granularity.tech/ru/docs/migration
- Last reviewed: 2026-10-27

**Мажорных переходов у библиотеки три.** С 2.x на 3.0 значения компонентов
переехали в темы: имена токенов — контракт ядра, значения даёт тема. С 1.x на 2.0
пришли темы и режимы: тема стала данными пакета токенов, светлый и тёмный — её
режимами, сборка — granum 3. С 0.x на 1.0 сменился конвейер, которым приложение
собирает CSS. Все три переезда ломающие, хотя ни один импорт компонента в них не
меняется.

## С 2.x на 3.0

Компоненты, их пропсы и внешний вид «Сланца» не меняются: значения, которые видят
компоненты в каждом режиме и острове, совпадают с 2.x. Меняется то, где эти
значения живут и кто отвечает за их полноту.

| Было в 2.x | Стало в 3.0 |
|---|---|
| peer `@feugene/granum` `>=3.8.0 <4.0.0` | `>=3.10.0 <4.0.0`; пакет токенов `@feugene/granularity-tokens` — мажор 2 |
| конфиг granum без `themes` собирал тему каталога по умолчанию | `themes` обязателен: основную тему выбирает приложение, без него — ошибка granum со списком тем каталога |
| `GR_DEFAULT_THEME_ID` | `GR_BASE_THEME_ID` — базовая тема пакета, а не основная тема чьей-то сборки |
| значения токенов компонентов — у самих компонентов | значения — в теме, разделом `components/@feugene/granularity/<GrX>/` |
| токен темы компонент читал с запасным значением | без запасного: нехватку называет `granum doctor` (`theme-incomplete`) |
| `--gr-button-neutral-bg` — хук, его задавала тема | токен темы; хук — ручка приложения, и в данных темы он — находка `theme-token-foreign` |
| `theme.json` с режимами массивом и `componentDefaults` в корне | режимы — объектом, пропсы по умолчанию — в `extensions.granularity.componentDefaults`, файл режима — плоские значения |

Свои значения поверх темы приложение задаёт как прежде — `modes.tokenOverrides`
записи темы или своим CSS; хук остаётся ручкой приложения поверх токена темы.

**Молча ломается одно** — своя тема без `extends`, которая опиралась на запасные
значения компонентов. Ошибки сборки нет: компонент просто не получает токен, а
`granum doctor` называет его поимённо. Значения дописываются в раздел
`components` темы, либо тема наследует тему каталога; начать с копии удобно
командой `gr-theme-copy slate <id>`. Тема с `extends: 'slate'` работает как прежде
и теперь получает от «Сланца» и значения компонентов.

Спутники `charts` и `media` перешли на ядро 3 мажорами 3.0: их значения тоже
живут в теме, и диапазон ядра у них — `>=3.1.0 <4.0.0`. `chrono`, `code`,
`dashboard`, `editor` и `forms-schema` своих значений у темы не держат и работают
с обоими мажорами ядра. Полная карта переезда — в
[документации библиотеки](https://github.com/efureev/granularity/blob/main/packages/granularity/docs/migration-3.md).

## С 1.x на 2.0

Компоненты, их пропсы и внешний вид «Сланца» не меняются: значения токенов
совпадают с 1.x, кроме двух исправленных дефектов тёмного острова — они описаны
в [журнале изменений](/ru/changelog). Меняется то, как тема попадает в сборку и
как переключается режим.

| Было в 1.x | Стало в 2.0 |
|---|---|
| peer `@feugene/granum` мажора 1 или 2 | `>=3.0.0 <4.0.0`, у выпусков с темами сборки — `>=3.8.0 <4.0.0`; движок `@feugene/granum-engine-wind` — тоже мажор 3 |
| `themes: { names: ['light', 'dark'] }` | `themes: [{ theme: 'slate', modes: ['light', 'dark'] }]` — список тем сборки; объект `themes` падает `InvalidConfigError` |
| `themes.define`, `themes.tokenOverrides` | `modes.define`, `modes.tokenOverrides` в записи темы |
| `data-theme="dark"` на корне | пара `data-theme="slate-dark"`; классов `.light` и `.dark` нет |
| `[data-theme='dark'] .x` в своём CSS | `[data-theme$='-dark'] .x` или пара целиком; `dark:` в утилитах работает без правок |
| `useTheme().theme` — имя режима | `theme` — текущая тема сборки; режим — `mode` |
| `setTheme('dark')`, `toggleTheme()` | `setMode('dark')`, `toggleMode()`; `setTheme(id)` переключает тему сборки |
| тип `ThemeName` | `GrModeName`, `GrThemeId` |
| `<GrConfigProvider theme="dark">` | `<GrConfigProvider mode="dark">`; `theme` — остров другой темы сборки |
| `@feugene/granularity/theme`: `extendTheme`, `createTheme` | `@feugene/granularity-tokens/theme`: `extendMode`, `createMode`, `defineTheme` |
| `@feugene/granularity/theme/apply` | `@feugene/granularity-tokens/theme/apply` |
| `grThemeTokens` из `@feugene/granularity/tokens` | `grRoleTokens` из `@feugene/granularity-tokens` |
| `styles/tokens.css`, `styles/themes/light.css` и `dark.css` | `themes/slate.css` — оба режима одной темы |

Манифест режимов отдают один раз, там, где создаётся состояние:
`initThemeEarly({ manifest })` или `app.use(granularityThemePlugin, { manifest })`
с `manifest` из `virtual:granum/themes`. Ключ хранилища прежний — `gr-theme`, и у
основной темы в нём по-прежнему имя режима: выбор пользователя переживает обновление.
Пакет токенов отдельно ставить не нужно — он приезжает зависимостью ядра.

**Молча ломается одно** — атрибут и селекторы с именем режима вместо пары.
`data-theme="dark"`, поставленный руками, и свой CSS под `[data-theme='dark']` не
дают ни ошибки, ни предупреждения: компоненты просто остаются в режиме по
умолчанию. `theme="dark"` у `GrConfigProvider` dev-сборка хотя бы подсветит
предупреждением.

Первые выпуски 2.x писали тему и режимы отдельными ключами — `theme: 'slate'` и
`modes: { names: [...] }`. С granum 3.8 это запись списка `themes`, а `previews` и
`css.previews` стали дополнительными темами со своими листами. Старые ключи granum
отвергает ошибкой конфига с подсказкой, куда переехало значение.

Спутники перешли на ядро 2 своими мажорами 2.x в тот же день: смешивать ядро 2 со
спутником, собранным под ядро 1, нельзя — манифесты granum 2 и 3 несовместимы.
Полная карта переезда — в
[документации библиотеки](https://github.com/efureev/granularity/blob/main/packages/granularity/docs/migration-2.md), ключи конфига granum —
в его руководстве ([ru](https://github.com/efureev/granum/blob/main/docs/ru/migration.md),
[en](https://github.com/efureev/granum/blob/main/docs/en/migration.md)).

## С 0.x на 1.0

| Было в 0.x | Стало в 1.0 |
|---|---|
| peer `@feugene/unocss-preset-granular` | peer `@feugene/granum` `>=1.0.0 <2.0.0` и движок `@feugene/granum-engine-wind` |
| `uno.config.ts` | `granum.config.ts`; движок передаётся инстансом — `engine: windEngine()` |
| CSS склеивался по порядку подключения | CSS приезжает модулем `virtual:granum.css` пятью слоями `granum.*` |
| сброс браузерных стилей — обычным CSS | сброс — в слое: `@import '…' layer(reset);`, иначе он перебьёт библиотеку |
| `defineGranularComponent`, `defineGranularProvider` | `defineGranumComponent`, `defineGranumProvider` из `@feugene/granum/contract` — для авторов провайдеров |

Спутники, которые зависят от ядра, перешли на 1.0 в тот же день: их диапазон
ядра теперь `>=1.0.0 <2.0.0`, и с ядром 0.x он не сходится. Поэтому ядро и
спутники обновляются вместе. Пошаговое руководство — в документации granum
([ru](https://github.com/efureev/granum/blob/main/docs/ru/migration.md),
[en](https://github.com/efureev/granum/blob/main/docs/en/migration.md)).
Ломающие правки внутри линии 0.x — в [журнале изменений](/ru/changelog).

## Что появится здесь к следующему мажору

:::steps
1. **Карта изменений API** — что переименовано, что удалено, что поменяло значение по умолчанию. Таблицей, а не прозой: по прозе не сделать поиск по имени пропа.
2. **Codemod.** Переименования выполняются скриптом, а не руками: он живёт в репозитории рядом с миграцией и запускается одной командой.
3. **Что ломается молча.** Отдельный раздел под изменения, которые не дают ни ошибки сборки, ни предупреждения, — сдвиг значения по умолчанию, смена порядка разрешения, другой ARIA-контракт. Это самая дорогая часть переезда и самая короткая в чужих гайдах. Один такой случай известен уже по 1.0 — сброс вне слоя, см. [типовые поломки](/ru/docs/troubleshooting).
4. **Порядок действий** для приложения, которое обновляется не сразу: что можно ставить рядом, что конфликтует, где проходит граница совместимости.
5. **Оценка трудоёмкости** по размеру приложения — чтобы решение «переезжать сейчас или в следующем квартале» принималось по числу, а не по настроению.
:::

## Версии документации

Документация одна и описывает линию 3.x; архива 0.x, 1.x и 2.x на портале нет. Версия в
шапке читается из манифеста установленного ядра на сборке и руками не пишется —
она совпадает с тем, на чём собран портал.

Подробности политики версий — на странице
[версионирования](/ru/docs/versioning).

:::callout{type="info"}
Переход **с другой библиотеки** — это не миграция в этом смысле. Карты
соответствия компонентов для него готовятся на отдельной странице —
[«Переход с другой библиотеки»](/ru/docs/migrating-from).
:::
