Header — Announcement Stack
blockmarketingTwo-tier site header: a dismissible announcement strip above a sticky navbar, where dismissing the strip collapses it and leaves the navbar flush against the viewport top.
Installation
$pnpm dlx uipkge-ng@latest add header-announcement-stack$npx uipkge-ng@latest add header-announcement-stack$yarn dlx uipkge-ng@latest add header-announcement-stack$bunx uipkge-ng@latest add header-announcement-stackManifest:
https://uipkge.dev/r/angular/header-announcement-stack.jsonInstalls to:src/app/components/blocks/header-announcement-stack/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
AnnouncementStackLinkinterface AnnouncementStackLink {
label: string
href: string
}npm dependencies
Files installed (2)
src/app/components/blocks/header-announcement-stack/header-announcement-stack.component.ts6.1 kB
// Two-tier site header: a dismissible announcement strip above a sticky navbar, // where dismissing the strip collapses it and leaves the navbar flush against the // viewport top. Port of the Vue/React block 1:1 — same links, same copy, same // session-scoped dismissal (rendered visible first so there is no flash of a strip // that then vanishes). import { ChangeDetectionStrategy, Component, Input, OnInit, signal } from '@angular/core' import { ArrowRight, Layers, LucideAngularModule, Menu, X } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' 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 AnnouncementStackLink { label: string href: string } const STORAGE_KEY = 'uipkge:announcement-dismissed' const links: AnnouncementStackLink[] = [ { label: 'Platform', href: '#platform' }, { label: 'Pricing', href: '#pricing' }, { label: 'Customers', href: '#customers' }, { label: 'Docs', href: '#docs' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-header-announcement-stack, [ui-header-announcement-stack]', standalone: true, // The block renders its own root <div>: keep this wrapper out of the layout. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiSeparatorComponent, UiSheetCloseComponent, UiSheetComponent, UiSheetContentComponent, UiSheetHeaderComponent, UiSheetTitleComponent, ], template: ` <div data-slot="header-announcement-stack" [class]="rootClass"> @if (!dismissed()) { <div class="border-border bg-muted/60 border-b backdrop-blur"> <div class="mx-auto flex h-10 max-w-6xl items-center gap-3 px-6 text-sm"> <span ui-badge variant="secondary" class="shrink-0">New</span> <p class="min-w-0 truncate"> Row-level scoping now evaluates against SCIM groups. <a href="#changelog" class="hover:text-foreground underline underline-offset-4">Read the changelog</a> </p> <lucide-icon [img]="ArrowRight" class="text-muted-foreground hidden size-3.5 shrink-0 sm:block" /> <button ui-button variant="ghost" size="icon" class="ml-auto size-7 shrink-0" aria-label="Dismiss announcement" (click)="dismiss()" > <lucide-icon [img]="X" class="size-3.5" /> </button> </div> </div> } <header class="border-border bg-background/90 border-b backdrop-blur"> <div class="mx-auto flex h-16 max-w-6xl items-center gap-6 px-6"> <a href="#top" class="flex shrink-0 items-center gap-2 font-semibold tracking-tight"> <lucide-icon [img]="Layers" class="text-primary size-5" /> Northwind </a> <nav class="hidden items-center gap-1 md:flex" aria-label="Primary"> @for (link of navLinks; track link.href) { <a [href]="link.href" class="text-muted-foreground hover:text-foreground hover:bg-muted 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" > {{ link.label }} </a> } </nav> <div class="ml-auto hidden items-center gap-2 md:flex"> <button ui-button variant="ghost" size="sm">Sign in</button> <button ui-button size="sm">Book a demo</button> </div> <div ui-sheet [open]="mobileOpen()" (openChange)="mobileOpen.set($event)"> <button ui-button variant="ghost" size="icon" class="ml-auto md:hidden" aria-label="Open navigation menu" (click)="mobileOpen.set(true)" > <lucide-icon [img]="Menu" class="size-5" /> </button> <div ui-sheet-content side="right" 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 navLinks; 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> <div class="grid gap-2"> <button ui-button variant="outline">Sign in</button> <button ui-button>Book a demo</button> </div> </div> </div> </div> </header> </div> `, }) export class UiHeaderAnnouncementStackComponent implements OnInit { protected readonly ArrowRight = ArrowRight protected readonly Layers = Layers protected readonly Menu = Menu protected readonly X = X protected readonly navLinks = links @Input('class') className?: string readonly dismissed = signal(false) readonly mobileOpen = signal(false) ngOnInit(): void { try { if (typeof window !== 'undefined' && typeof window.sessionStorage !== 'undefined') { this.dismissed.set(window.sessionStorage.getItem(STORAGE_KEY) === '1') } } catch { this.dismissed.set(false) } } dismiss(): void { this.dismissed.set(true) try { window.sessionStorage.setItem(STORAGE_KEY, '1') } catch { // Blocked storage: the dismissal still holds for this page view. } } get rootClass(): string { return cn('sticky top-0 z-40', this.className) } }src/app/components/blocks/header-announcement-stack/index.ts0.1 kB
export { UiHeaderAnnouncementStackComponent, type AnnouncementStackLink } from './header-announcement-stack.component'
Raw manifest:https://uipkge.dev/r/angular/header-announcement-stack.json