GrNavbar
Берут, когда у приложения есть верхняя панель.
Когда брать
- у приложения есть верхняя панель — заголовок, поиск, профиль, уведомления в трёх зонах;
- панель должна оставаться видимой —
stickyпри прокрутке длинных страниц; - на мобильном нужна кнопка меню —
showMenuButtonоткрываетGrDrawerилиGrSidebar; - нужен лендмарк
banner— корень рендерится тегом<header>, и обход по ориентирам работает.
Когда взять другое
| Нужно | Берите |
|---|---|
| Боковая навигация | GrSidebar |
| Нижняя панель разделов на мобильном | GrBottomNav |
| Панель действий над таблицей или дашбордом | GrButtonGroup / GrDashboardToolbar |
| Путь до текущей страницы | GrBreadcrumbs |
Три зоны
<GrNavbar>
<template #title>
<RouterLink to="/">Granularity</RouterLink>
</template>
<template #center>
<GrInput placeholder="Поиск" size="sm" aria-label="Поиск" />
</template>
<GrButton size="sm">Опубликовать</GrButton>
</GrNavbar>
| Слот | Где |
|---|---|
title | слева, после кнопки меню; заменяет строку title |
left | сразу за заголовком — вкладки, переключатель раздела |
center | по центру панели — поиск, хлебные крошки |
| по умолчанию | справа — действия, аватар |
Проп title необязателен: заголовок собирается разметкой через слот. Когда нет
ни строки, ни слота, блок заголовка не рендерится и не съедает отступ.
Центральная зона появляется только вместе со слотом. При ней боковые зоны делят остаток поровну — иначе «центр» считался бы от остатка и уезжал вслед за более широким боком.
Прилипание
<GrNavbar sticky />
sticky оставляет панель наверху при прокрутке. Слой — --gr-z-navbar (900):
ниже якорных панелей (--gr-z-dropdown — 1000), поэтому открытый список,
тултип или модалка панель перекрывают, а не уезжают под неё. Подробности —
../z-index.md.
Высота
Задаётся переменной --gr-navbar-height (по умолчанию 56px) — на самой
панели, на предке или в теме:
.app-shell { --gr-navbar-height: 48px; }Кнопка меню
showMenuButton показывает кнопку и эмитит menu; её доступное имя приходит из
локали (gr.navbar.openMenu). menuButtonClass нужен, чтобы прятать кнопку на
широких экранах (sm:hidden) — панель не решает за приложение, когда меню
сворачивается.
size задаёт ступень только этой кнопки — остальное в шапке принадлежит
потребителю, а высоту ряда держит --gr-navbar-height. Читается из
GrConfigProvider (componentDefaults.GrNavbar.size).
Фолбэк — sm (32px), а не общий для пакета md: кнопка была этого размера с
самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен полноценный
тач-таргет — size="lg" даёт 44px и помещается в ряд высотой 56px.
Playground 5
Загружается…
<GrNavbar />Установка
npm i @feugene/granularityИмпорт
import { GrNavbar } from '@feugene/granularity/components/GrNavbar'API
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
title | string | undefined | undefined | Заголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Ступень кнопки меню — и только её: остальное в шапке принадлежит потребителю, а высоту ряда задаёт `--gr-navbar-height`. Фолбэк — `sm` (32px), а не общий для пакета `md`: кнопка была этого размера с самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен тач-таргет — `size="lg"` даёт 44px, ровно то, чего требуют WCAG 2.5.5 и Apple HIG; в 56px ряда он помещается. |
showMenuButton | boolean | undefined | false | — |
menuButtonClass | string | undefined | "" | Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). |
sticky | boolean | undefined | false | Панель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже якорных панелей, чтобы открытый список не уезжал под шапку. |
Slots
| Slot | Type | Описание |
|---|---|---|
title | any | Заголовок целиком — вместо строки `title`. |
left | any | Зона сразу после заголовка: вкладки, переключатель раздела. |
center | any | Центральная зона: поиск, хлебные крошки. |
default | any | Правая зона: действия, аватар. |
Events
| Event | Type | Описание |
|---|---|---|
menu | [] | — |
Примеры 3
Слот действий в оболочке страницы
Показываем основной layout-случай: GrNavbar задаёт title area, а справа размещаются глобальные quick actions и status pills.
Правый слот — для глобальных действий уровня приложения: поиск, уведомления, аккаунт. Для навигации по разделам используйте GrSidebar (десктоп) или GrBottomNav (мобайл), а не сам хедер.
<script setup lang="ts">
import { ref } from 'vue'
import { GrAvatar, GrBadge, GrButton, GrNavbar } from '@feugene/granularity'
const notifications = ref(3)
</script>
<template>
<!--
GrNavbar — это ГЛОБАЛЬНЫЙ верхний хедер приложения (`<header>`-landmark),
который тянется на всю ширину над маршрутизируемым контентом. Здесь он показан
в контексте настоящего app-shell: хедер сверху + контент под ним.
Не путайте с секционными заголовками страниц и marketing-навигацией.
-->
<div class="overflow-hidden rounded-2xl border border-[var(--gr-brd)] bg-[var(--gr-bg)] shadow-[var(--gr-shadow-1)]">
<GrNavbar title="Acme Console">
<GrButton size="sm" variant="ghost" square aria-label="Search">
<span class="i-lucide-search block h-4 w-4" aria-hidden="true" />
</GrButton>
<GrButton size="sm" variant="ghost" square aria-label="Notifications" @click="notifications = 0">
<span class="relative inline-flex">
<span class="i-lucide-bell block h-4 w-4" aria-hidden="true" />
<GrBadge v-if="notifications" size="xs" tone="danger" dark class="absolute -right-2 -top-2">
{{ notifications }}
</GrBadge>
</span>
</GrButton>
<GrAvatar :size="28" alt="Ada Lovelace" />
</GrNavbar>
<!-- Условный контент приложения под хедером -->
<div class="grid gap-3 p-5">
<div class="h-3 w-40 rounded bg-[var(--gr-muted)]" />
<div class="grid gap-2 sm:grid-cols-3">
<div class="h-16 rounded-lg border border-[var(--gr-brd)] bg-[var(--gr-card)]" />
<div class="h-16 rounded-lg border border-[var(--gr-brd)] bg-[var(--gr-card)]" />
<div class="h-16 rounded-lg border border-[var(--gr-brd)] bg-[var(--gr-card)]" />
</div>
</div>
</div>
<p class="mt-3 text-sm text-[var(--gr-muted-fg)]">
Правый слот — для глобальных действий уровня приложения: поиск, уведомления, аккаунт.
Для навигации по разделам используйте <code>GrSidebar</code> (десктоп) или
<code>GrBottomNav</code> (мобайл), а не сам хедер.
</p>
</template>Кнопка меню на узком экране
Показываем showMenuButton, menuButtonClass и событие menu для responsive drawer/navigation shells.
<script setup lang="ts">
import { ref } from 'vue'
import { GrNavbar } from '@feugene/granularity'
const isMenuOpen = ref(false)
const navItems = ['Overview', 'Deployments', 'Billing', 'Settings']
</script>
<template>
<!--
Граница применения: кнопка-гамбургер (`show-menu-button`) нужна ТОЛЬКО в
компактном/мобильном режиме, когда постоянный `GrSidebar` скрыт. Событие `menu`
открывает off-canvas панель навигации. На десктопе кнопку прячут (`sm:hidden`),
а разделы живут в боковой панели.
-->
<div class="relative overflow-hidden rounded-2xl border border-[var(--gr-brd)] bg-[var(--gr-bg)] shadow-[var(--gr-shadow-1)]">
<GrNavbar
title="Mobile shell"
show-menu-button
@menu="isMenuOpen = !isMenuOpen"
/>
<div class="relative min-h-[160px]">
<!-- Off-canvas панель навигации, которую открывает кнопка меню -->
<transition
enter-active-class="transition-transform duration-200 ease-out"
enter-from-class="-translate-x-full"
leave-active-class="transition-transform duration-150 ease-in"
leave-to-class="-translate-x-full"
>
<nav
v-if="isMenuOpen"
class="absolute inset-y-0 left-0 z-10 w-52 border-r border-[var(--gr-brd)] bg-[var(--gr-card)] p-3"
>
<button
v-for="item in navItems"
:key="item"
type="button"
class="block w-full rounded-lg px-3 py-2 text-left text-sm text-[var(--gr-fg)] transition-colors hover:bg-[var(--gr-muted)]"
@click="isMenuOpen = false"
>
{{ item }}
</button>
</nav>
</transition>
<div class="p-5 text-sm text-[var(--gr-muted-fg)]">
{{ isMenuOpen ? 'Navigation drawer is open — pick a section.' : 'Tap the hamburger to open the navigation drawer.' }}
</div>
</div>
</div>
</template>Свой слот заголовка
Кастомный title-slot нужен для брендинга, breadcrumbs и richer header-контекста без форка базового layout-компонента.
#title (брендинг, статус-бейдж) и #left, по центру #center — поиск или хлебные крошки, справа слот по умолчанию. Проп title не нужен, когда заголовок собран разметкой. С sticky панель остаётся сверху при прокрутке, но не перекрывает выпадающие панели. <script setup lang="ts">
import { GrBadge, GrButton, GrInput, GrNavbar } from '@feugene/granularity'
</script>
<template>
<div class="grid gap-3 rounded-xl border border-[var(--gr-brd)] bg-[var(--gr-bg)] p-3">
<GrNavbar sticky>
<template #title>
<div class="flex items-center gap-2">
<span class="text-sm font-semibold">Release dashboard</span>
<GrBadge size="sm" tone="info">
Beta
</GrBadge>
</div>
</template>
<template #center>
<GrInput placeholder="Search releases" size="sm" aria-label="Search releases" />
</template>
<GrButton size="sm">
Publish
</GrButton>
</GrNavbar>
<div class="px-4 py-3 text-sm text-[var(--gr-muted-fg)]">
Три зоны хедера: слева <code>#title</code> (брендинг, статус-бейдж) и <code>#left</code>,
по центру <code>#center</code> — поиск или хлебные крошки, справа слот по умолчанию.
Проп <code>title</code> не нужен, когда заголовок собран разметкой. С <code>sticky</code>
панель остаётся сверху при прокрутке, но не перекрывает выпадающие панели.
</div>
</div>
</template>