GrNavbar

Пакет: @feugene/granularityядроГруппа: Навигация

Берут, когда у приложения есть верхняя панель.

Когда брать

  • у приложения есть верхняя панель — заголовок, поиск, профиль, уведомления в трёх зонах;
  • панель должна оставаться видимой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

PropTypeпо умолчаниюОписание
titlestring | undefinedundefinedЗаголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedСтупень кнопки меню — и только её: остальное в шапке принадлежит потребителю, а высоту ряда задаёт `--gr-navbar-height`. Фолбэк — `sm` (32px), а не общий для пакета `md`: кнопка была этого размера с самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен тач-таргет — `size="lg"` даёт 44px, ровно то, чего требуют WCAG 2.5.5 и Apple HIG; в 56px ряда он помещается.
showMenuButtonboolean | undefinedfalse
menuButtonClassstring | undefined""Extra classes applied to the menu button wrapper (e.g. `sm:hidden`).
stickyboolean | undefinedfalseПанель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже якорных панелей, чтобы открытый список не уезжал под шапку.

Slots

SlotTypeОписание
titleanyЗаголовок целиком — вместо строки `title`.
leftanyЗона сразу после заголовка: вкладки, переключатель раздела.
centeranyЦентральная зона: поиск, хлебные крошки.
defaultanyПравая зона: действия, аватар.

Events

EventTypeОписание
menu[]

Примеры 3

Слот действий в оболочке страницы

Показываем основной layout-случай: GrNavbar задаёт title area, а справа размещаются глобальные quick actions и status pills.

Acme Console

Правый слот — для глобальных действий уровня приложения: поиск, уведомления, аккаунт. Для навигации по разделам используйте GrSidebar (десктоп) или GrBottomNav (мобайл), а не сам хедер.

Actions
<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.

Mobile shell
Tap the hamburger to open the navigation drawer.

Menu Toggle
<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-компонента.

Release dashboard Beta
Три зоны хедера: слева #title (брендинг, статус-бейдж) и #left, по центру #center — поиск или хлебные крошки, справа слот по умолчанию. Проп title не нужен, когда заголовок собран разметкой. С sticky панель остаётся сверху при прокрутке, но не перекрывает выпадающие панели.

Title Slot
<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>

Документация компонентаВсе компоненты