---
title: "Whitelabel-навігація"
description: "Закрита мапа іконок Lucide, перезапис primary-nav у паку та Layer3 overlay remap наявних слотів"
locale: "uk"
---
# Whitelabel-навігація

> **Info**
> Використовуйте вкладки **Founder** / **Developer** у бічній панелі docs, щоб фільтрувати цю сторінку. Спільні розділи стосуються обох аудиторій.

> **Warning**
> **Статус:** Layer1 chrome — це **data manifest + закрита мапа іконок**, а не варіанти `*-desktop-navigation.tsx` на клон. Файлу `config/navigation.config.ts` немає. Паки перезаписують `lib/navigation/primary-nav.ts`. Клони ремаплять **наявні** id слотів у `lib/navigation/primary-nav-overlay.ts`. Невідомі icon id рендерять гліф `users`.

| Попередня звичка | Еквівалент у Ring |
|----------------|-----------------|
| Форкати `vikka-desktop-navigation.tsx` / `greenfood-navigation.tsx` | L2 пак перезаписує `lib/navigation/primary-nav.ts` (слоти, hrefs, icon **ids**) |
| Імпортувати вертикально-іменовану Lucide-іконку в chrome | Додати **generic** id до `PRIMARY_NAV_ICONS` на Layer1, потім посилатися на цей id |
| Overlay нового mobile tab з клону | Overlay **не може** додавати, ховати чи змінювати порядок слотів — змінюйте маніфест паку |
| Копіювати `desktop-sidebar.tsx` на клон | Тримайте chrome на Layer1; ремапте лише `iconById` / `labelKeysById` / `hrefById` |

## Архітектура

```mermaid
flowchart TD
  Pack["L2 pack overwritesprimary-nav.ts"] --> Manifest["getPrimaryNavManifest"]
  L1["L1 BASE_MANIFEST"] --> Manifest
  Overlay["L3 primary-nav-overlay.tsremap existing ids"] --> Manifest
  Manifest --> Icons["getPrimaryNavIconprimary-nav-icons.ts"]
  Icons --> Mobile["bottom-navigation.tsx"]
  Icons --> Rail["sidebar-rail / sidebar-synced-layout"]
```

`home.preset` **не** обирає меню. Overflow-модулі (каталог `[...]`) живуть у `lib/navigation/platform-menu.ts` плюс `ring-config.navigation.platformMenu`. Паки **не повинні** перезаписувати `platform-menu.ts`; вертикальні дефолти належать у `PACK.json.platformMenu` і копіюються на `ring-config` клону.

### For founders

## Що можна змінити без форку chrome

- **Підписи** — ключі next-intl на кожному слоті (`labelKeys`). `pack.json` паку та overlay клону можуть перевизначати листки. Див. [Localization](/docs/customization/localization.md).
- **Який гліф Lucide** — оберіть id, який уже є в закритій мапі (таблиця нижче). Новий гліф — це зміна **Layer1**, не файл клону.
- **Href наявного слота** — L3 `hrefById` на id цього слота (той самий контракт locale → string, що й у items паку).
- **Форма mobile bar** — перші два href з маніфесту, center plus (add opportunity), далі решта. Пак, який перелічує три href плюс overflow (і пропускає docs-or-admin), отримує цей bar з тих самих chrome slices. Overlay не може додавати чи ховати ці слоти.

Не просіть інтегратора класти кастомний sidebar-компонент у `components/navigation/` заради brand icons.

### For developers

## Закрита мапа іконок

Шлях: `lib/navigation/primary-nav-icons.ts`. Chrome викликає `getPrimaryNavIcon(id)`. Паки та overlays передають лише **string ids**.

| Id | Lucide export |
|----|----------------|
| `users` | `Users` (fallback for unknown ids) |
| `briefcase` | `Briefcase` |
| `store` | `Store` |
| `user` | `UserRound` |
| `file-text` | `FileText` |
| `ellipsis` | `CircleEllipsis` |
| `more-horizontal` | `MoreHorizontal` |
| `shopping-basket` | `ShoppingBasket` |
| `shopping-bag` | `ShoppingBag` |
| `building` | `Building2` |
| `newspaper` | `Newspaper` |
| `tv` | `Tv` |
| `sparkles` | `Sparkles` |
| `gamepad` | `Gamepad2` |
| `hexagon` | `Hexagon` |
| `map` | `Map` |
| `layout-grid` | `LayoutGrid` |
| `wallet` | `Wallet` |
| `bell` | `Bell` |
| `heart` | `Heart` |
| `shopping-cart` | `ShoppingCart` |
| `message-circle` | `MessageCircle` |
| `list-todo` | `ListTodo` |
| `settings` | `Settings` |
| `sun` | `Sun` |
| `orbit` | `Orbit` |
| `calendar` | `Calendar` |
| `scale` | `Scale` |
| `hash` | `Hash` |
| `type` | `Type` |
| `moon` | `Moon` |
| `compass` | `Compass` |

Додайте generic гліф **сюди**, перш ніж пак або overlay посилатиметься на новий id. Не накладайте цей файл іконками з іменами клону.

## Overlay socket (лише remap)

Layer1 постачає `export const PRIMARY_NAV_OVERLAY = {}` у `lib/navigation/primary-nav-overlay.ts`, щоб webpack завжди резолвив імпорт. Клон перезаписує **цей файл** (не `primary-nav.ts`) так:

{`import { ROUTES } from '@/constants/routes'

export const PRIMARY_NAV_OVERLAY = {
  iconById: { entities: 'sun' },
  labelKeysById: { entities: ['customEntities'] },
  hrefById: { entities: (locale) => ROUTES.ENTITIES(locale) },
}`}

`getPrimaryNavManifest()` проганяє `BASE_MANIFEST` через `applyHrefOverlay` / `applyMobileOverlay`. Id, яких **немає** в маніфесті паку/L1, ігноруються.

## Mobile chrome

`components/navigation/bottom-navigation.tsx` рендерить `navItems.slice(0, 2)`, потім center add control, потім `navItems.slice(2)`. Компоненти іконок беруться з `getPrimaryNavIcon`. Desktop rail: `sidebar-rail.tsx` і `sidebar-synced-layout.tsx` використовують той самий helper.

Оркестратор: `components/navigation/navigation.tsx` динамічно імпортує `desktop-sidebar.tsx` і `bottom-navigation.tsx`. Публічного реєстру варіантів `DesktopNavigation` немає.

## Рекомендований шлях

Потрібен новий гліф? Спочатку зафіксуйте його на Layer1 (`ring/web/lib/navigation/primary-nav-icons.ts`), потім вкажіть id у слоті паку. Cursor skill **`layer2-preset-operator`** володіє pack `primary-nav.ts`; **`ringization-implementer`** володіє L3 overlay remap. Жоден не повинен копіювати chrome.

**Стартовий промпт (нічого не змінює, доки ви не схвалите):**

> Add generic Lucide id `` to Layer1 `lib/navigation/primary-nav-icons.ts` before pack `` references it. Do not import vertical-named icons in chrome. Overlay may only remap existing slot ids in `primary-nav-overlay.ts`.

## Ручний шлях

Підтвердіть, що id слота існує в складеному маніфесті `primary-nav.ts` (масиви `mobile` / `desktop`). Overlay не може вигадувати id.

Якщо icon id відсутній у `PRIMARY_NAV_ICONS`, додайте Lucide-імпорт і запис мапи на Layer1. Невідомі id тихо падають назад на `users`.

Встановіть `icon` на слоті паку **або** `PRIMARY_NAV_OVERLAY.iconById[slotId]` на клоні. Тримайте `platform-menu.ts` на Layer1; вертикальні overflow-дефолти кладіть у `PACK.json.platformMenu`.

## Часті запитання

### Вплив

#### Чи може клон сховати вкладку Store лише через overlay?

Ні. Приберіть або замініть слот у **pack** `primary-nav.ts` (compose overwrite). Overlay лише патчить `icon` / `labelKeys` / `href` на id, які вже існують.

#### Що станеться, якщо пак використовує `sun`, перш ніж Layer1 його перелічить?

`getPrimaryNavIcon` повертає `Users`. Bar не падає; гліф неправильний, доки Layer1 не завезе id.

### Міграція

#### У нас досі є clone-local sidebar-компонент. Що замість?

Видаліть форк. Направте compose на pack `primary-nav.ts` + empty-or-remap `primary-nav-overlay.ts`. Файли chrome лишаються на Layer1.

### Ops

#### Хто володіє `platform-menu.ts`?

Layer1. Паки не повинні rsync поверх нього. Clone `ring-config.navigation.platformMenu` (`items` / `exclude` / `extra`) — це важіль overflow-каталогу.

## Повʼязана документація

  
- [customization/vertical-presets](/docs/customization/vertical-presets.md) — Depends-on: L1 / L2 pack / L3 overlay compose and pack.json socket.

  
- [customization/localization](/docs/customization/localization.md) — Same-workflow: nav labelKeys resolve through Layer1 → pack → overlay messages.

  
- [customization/ringization-playbook](/docs/customization/ringization-playbook.md) — Prerequisite: chrome stays on Layer1; product stays on pack or clone overlay.

  
- [customization/customization-guide](/docs/customization/customization-guide.md) — See-also: ring-config feature flags that hide routes from nav.

  
- [features/locale-system](/docs/features/locale-system.md) — Next-step: locale env used by href builders and next-intl.
