Menu

Change language

GitHub repo

Header — Centered Logo

blockmarketing

Symmetrical navbar with the wordmark centred between two nav halves, a utility cluster on the right, and a mobile layout that collapses the split into a single sheet trigger.

Installation

$npx uipkge-ng@latest add header-centered-logo
Manifest:https://uipkge.dev/r/angular/header-centered-logo.jsonInstalls to:src/app/components/blocks/header-centered-logo/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

CenteredLogoLink
interface CenteredLogoLink {
  label: string
  href: string
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/header-centered-logo/header-centered-logo.component.ts5 kB
    // Symmetrical navbar with the wordmark centred between two nav halves, a utility
    // cluster on the right, and a mobile layout collapsing the split into a single
    // sheet trigger. Port of the Vue/React block 1:1 — equal-basis flanks keep the
    // wordmark optically centred regardless of label length.
    import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'
    import { Compass, LucideAngularModule, Menu, ShoppingBag, User } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    import {
      UiSheetCloseComponent,
      UiSheetComponent,
      UiSheetContentComponent,
      UiSheetHeaderComponent,
      UiSheetTitleComponent,
    } from '@/ui/sheet/sheet.component'
    
    export interface CenteredLogoLink {
      label: string
      href: string
    }
    
    const leftLinks: CenteredLogoLink[] = [
      { label: 'Collections', href: '#collections' },
      { label: 'New in', href: '#new' },
      { label: 'Studio', href: '#studio' },
    ]
    
    const rightLinks: CenteredLogoLink[] = [
      { label: 'Journal', href: '#journal' },
      { label: 'Stockists', href: '#stockists' },
      { label: 'Contact', href: '#contact' },
    ]
    
    const linkClass =
      'text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none'
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-header-centered-logo, [ui-header-centered-logo]',
      standalone: true,
      // The block renders its own <header>: keep this wrapper out of the layout.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiButtonComponent,
        UiSeparatorComponent,
        UiSheetCloseComponent,
        UiSheetComponent,
        UiSheetContentComponent,
        UiSheetHeaderComponent,
        UiSheetTitleComponent,
      ],
      template: `
        <header data-slot="header-centered-logo" [class]="rootClass">
          <div class="mx-auto flex h-16 max-w-6xl items-center px-6">
            <!-- Equal-basis flanks keep the wordmark optically centred regardless of
                 how long the link labels are. -->
            <nav class="hidden flex-1 basis-0 items-center gap-1 lg:flex" aria-label="Primary left">
              @for (link of left; track link.href) {
                <a [href]="link.href" [class]="desktopLinkClass">{{ link.label }}</a>
              }
            </nav>
    
            <div ui-sheet [open]="mobileOpen()" (openChange)="mobileOpen.set($event)">
              <button
                ui-button
                variant="ghost"
                size="icon"
                class="lg:hidden"
                aria-label="Open navigation menu"
                (click)="mobileOpen.set(true)"
              >
                <lucide-icon [img]="Menu" class="size-5" />
              </button>
              <div ui-sheet-content side="left" class="w-72">
                <div ui-sheet-header>
                  <h2 ui-sheet-title>Menu</h2>
                </div>
                <nav class="mt-6 grid gap-1" aria-label="Mobile">
                  @for (link of allLinks(); track link.href) {
                    <a
                      [href]="link.href"
                      class="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
                      (click)="mobileOpen.set(false)"
                    >
                      {{ link.label }}
                    </a>
                  }
                </nav>
                <div ui-separator class="my-5"></div>
                <button ui-button class="w-full">Account</button>
              </div>
            </div>
    
            <a
              href="#top"
              class="mx-auto flex items-center gap-2 text-base font-semibold tracking-[0.18em] uppercase lg:mx-6"
            >
              <lucide-icon [img]="Compass" class="text-primary size-4" />
              Halden
            </a>
    
            <nav class="hidden flex-1 basis-0 items-center justify-end gap-1 lg:flex" aria-label="Primary right">
              @for (link of right; track link.href) {
                <a [href]="link.href" [class]="desktopLinkClass">{{ link.label }}</a>
              }
            </nav>
    
            <div class="flex items-center gap-1 lg:ml-4">
              <button ui-button variant="ghost" size="icon" aria-label="Account">
                <lucide-icon [img]="User" class="size-4" />
              </button>
              <button ui-button variant="ghost" size="icon" aria-label="Bag">
                <lucide-icon [img]="ShoppingBag" class="size-4" />
              </button>
            </div>
          </div>
        </header>
      `,
    })
    export class UiHeaderCenteredLogoComponent {
      protected readonly Compass = Compass
      protected readonly Menu = Menu
      protected readonly ShoppingBag = ShoppingBag
      protected readonly User = User
    
      protected readonly left = leftLinks
      protected readonly right = rightLinks
      protected readonly desktopLinkClass = linkClass
    
      @Input('class') className?: string
    
      readonly mobileOpen = signal(false)
    
      allLinks(): CenteredLogoLink[] {
        return [...this.left, ...this.right]
      }
    
      get rootClass(): string {
        return cn('border-border bg-background border-b', this.className)
      }
    }
    
  • src/app/components/blocks/header-centered-logo/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/header-centered-logo.json