Footer
blockmarketingSite footer collection: multi-column newsletter, oversized brand statement, dense developer grid, mega newsletter, single-row minimal, and dense sitemap variants — each spelled out inline so copy lives in one place.
Installation
$pnpm dlx uipkge-ng@latest add footer$npx uipkge-ng@latest add footer$yarn dlx uipkge-ng@latest add footer$bunx uipkge-ng@latest add footerManifest:
https://uipkge.dev/r/angular/footer.jsonInstalls to:src/app/components/blocks/footer/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.
BrandStatementSectioninterface BrandStatementSection {
heading: string
links: string[]
}DeveloperFooterColumninterface DeveloperFooterColumn {
title: string
links: { label: string; href: string; badge?: string }[]
}MegaSectioninterface MegaSection {
heading: string
links: string[]
}SitemapSectioninterface SitemapSection {
heading: string
links: string[]
}Files installed (2)
src/app/components/blocks/footer/footer.component.ts31.2 kB
// Site footer collection: multi-column newsletter, oversized brand statement, dense // developer grid, mega newsletter, single-row minimal, and dense sitemap variants — each // spelled out inline so copy lives in one place. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { ArrowRight, Boxes, Check, Github, Globe, Layers, Linkedin, LucideAngularModule, Twitter, Youtube, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiInputComponent } from '@/ui/input/input.component' import { UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, } from '@/ui/select/select.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' // --------------------------------------------------------------------------- // Footer 01: brand + newsletter on the left, four link columns on the right. // --------------------------------------------------------------------------- @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-01, [ui-footer-01]', standalone: true, // React renders the root <footer> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiButtonComponent, UiInputComponent, UiSeparatorComponent], template: ` <footer data-slot="footer-01" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-16"> <div class="grid gap-10 lg:grid-cols-12"> <div class="space-y-4 lg:col-span-4"> <div class="flex items-center gap-2"> <div class="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md"> <lucide-icon [img]="Boxes" class="size-4" /> </div> <span class="text-base font-semibold">Acme</span> </div> <p class="text-muted-foreground max-w-sm text-sm">Built on shadcn-vue. Get product updates monthly.</p> <form class="flex max-w-sm gap-2" (submit)="subscribe($event)"> <ui-input [value]="newsletter()" (valueChange)="newsletter.set($event)" type="email" placeholder="[email protected]" required class="flex-1" /> <button ui-button type="submit">Subscribe</button> </form> @if (subscribed()) { <p class="text-success text-xs">Thanks — check your inbox to confirm.</p> } </div> <div class="grid grid-cols-2 gap-6 sm:grid-cols-4 lg:col-span-8"> <div class="space-y-3"> <h3 class="text-sm font-semibold">Product</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Features</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Pricing</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors" >Changelog</a > </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Roadmap</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Company</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">About</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Blog</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Careers</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Contact</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Resources</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> Documentation </a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">API</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> Help center </a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Status</a> </li> </ul> </div> <div class="space-y-3"> <h3 class="text-sm font-semibold">Legal</h3> <ul class="space-y-2"> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Terms</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Privacy</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">Security</a> </li> <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors">DPA</a> </li> </ul> </div> </div> </div> <div ui-separator class="my-10"></div> <div class="flex flex-wrap items-center justify-between gap-4"> <p class="text-muted-foreground text-xs">© 2026 Acme. All rights reserved.</p> <div class="flex items-center gap-3"> <a href="#" aria-label="GitHub" class="text-muted-foreground hover:text-foreground transition-colors"> <lucide-icon [img]="Github" class="size-4" /> </a> <a href="#" aria-label="Twitter" class="text-muted-foreground hover:text-foreground transition-colors"> <lucide-icon [img]="Twitter" class="size-4" /> </a> <a href="#" aria-label="LinkedIn" class="text-muted-foreground hover:text-foreground transition-colors"> <lucide-icon [img]="Linkedin" class="size-4" /> </a> </div> </div> </div> </footer> `, }) export class UiFooter01Component { protected readonly Boxes = Boxes protected readonly Github = Github protected readonly Linkedin = Linkedin protected readonly Twitter = Twitter @Input('class') className?: string readonly newsletter = signal('') readonly subscribed = signal(false) get rootClass(): string { return cn('bg-background border-t', this.className) } subscribe(event?: Event): void { event?.preventDefault() if (!this.newsletter()) return this.subscribed.set(true) this.newsletter.set('') } } // --------------------------------------------------------------------------- // Footer — Brand Statement: oversized statement, three columns, legal row. // --------------------------------------------------------------------------- interface BrandStatementSection { heading: string links: string[] } const brandStatementSections: BrandStatementSection[] = [ { heading: 'Platform', links: ['Metric layer', 'Governance', 'Alerting', 'Embedding'] }, { heading: 'Company', links: ['About', 'Careers', 'Customers', 'Press kit'] }, { heading: 'Resources', links: ['Documentation', 'Guides', 'Benchmarks', 'Status'] }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-brand-statement, [ui-footer-brand-statement]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiSeparatorComponent], template: ` <footer data-slot="footer-brand-statement" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-16"> <a href="#top" class="flex w-fit items-center gap-2 font-semibold tracking-tight"> <lucide-icon [img]="Layers" class="text-primary size-5" aria-hidden="true" /> Northwind </a> <!-- The statement is the only oversized type on the page. Keep it to one sentence; at this size a second one stops being read. --> <p class="mt-8 max-w-3xl text-2xl leading-tight font-medium tracking-tight text-balance sm:text-3xl"> Every number your company acts on should have one definition, one owner, and a history you can read. </p> <p class="text-muted-foreground mt-4 max-w-xl text-sm leading-relaxed"> That is the whole product. Everything else here is detail. </p> <div ui-separator class="my-12"></div> <div class="grid grid-cols-2 gap-8 sm:grid-cols-3"> @for (section of sectionList; track section.heading) { <div> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase">{{ section.heading }}</p> <ul class="mt-3 space-y-2"> @for (link of section.links; track link) { <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> {{ link }} </a> </li> } </ul> </div> } </div> <div ui-separator class="my-10"></div> <div class="flex flex-col gap-4 sm:flex-row sm:items-center"> <div class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 text-xs"> <span>© 2026 Northwind Data, Inc.</span> <a href="#privacy" class="hover:text-foreground transition-colors">Privacy</a> <a href="#terms" class="hover:text-foreground transition-colors">Terms</a> <a href="mailto:[email protected]" class="hover:text-foreground transition-colors"> hello@northwind.example </a> </div> <div class="flex items-center gap-2 sm:ml-auto"> <span ui-badge variant="outline">SOC 2 Type II</span> <span ui-badge variant="outline">GDPR</span> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <lucide-icon [img]="Github" class="size-4" aria-hidden="true" /> </button> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <lucide-icon [img]="Linkedin" class="size-4" aria-hidden="true" /> </button> </div> </div> </div> </footer> `, }) export class UiFooterBrandStatementComponent { protected readonly Github = Github protected readonly Layers = Layers protected readonly Linkedin = Linkedin protected readonly sectionList = brandStatementSections @Input('class') className?: string get rootClass(): string { return cn('border-border bg-background border-t', this.className) } } // --------------------------------------------------------------------------- // Footer — Developer Dense Grid: categorized columns + telemetry status bar. // --------------------------------------------------------------------------- export interface DeveloperFooterColumn { title: string links: { label: string; href: string; badge?: string }[] } const developerColumns: DeveloperFooterColumn[] = [ { title: 'UI Primitives', links: [ { label: 'Button & Controls', href: '#' }, { label: 'Dialog & Overlays', href: '#' }, { label: 'Data Table & Virtual', href: '#' }, { label: 'Charts & Sparklines', href: '#' }, { label: 'Form Controls & Inputs', href: '#' }, { label: 'KpiGrid & Metric Tiles', href: '#' }, ], }, { title: 'Marketing Blocks', links: [ { label: 'Hero Sandboxes', href: '#' }, { label: 'Feature Workbenches', href: '#' }, { label: 'Pricing Matrix & ROI', href: '#' }, { label: 'Social Proof Tickers', href: '#' }, { label: 'Testimonial Masonry', href: '#' }, { label: 'Interactive Demos', href: '#', badge: 'New' }, ], }, { title: 'Ecosystem Templates', links: [ { label: 'HRMS Nuxt Template', href: '#' }, { label: 'HMS Clinical Dashboard', href: '#' }, { label: 'Shipment Tracking Live', href: '#' }, { label: 'Fintech Banking Portal', href: '#' }, { label: 'AI LLM Workspace', href: '#' }, ], }, { title: 'Developer DX', links: [ { label: 'CLI Installation Guide', href: '#' }, { label: 'Tailwind CSS v4 Tokens', href: '#' }, { label: 'Dual-Framework Parity', href: '#' }, { label: 'Reka UI Primitives', href: '#' }, { label: 'Radix UI Primitives', href: '#' }, { label: 'Changelog Timeline', href: '#' }, ], }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-developer-dense-grid, [ui-footer-developer-dense-grid]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiButtonComponent], template: ` <footer data-slot="footer-developer-dense-grid" [class]="rootClass"> <div class="mx-auto max-w-7xl space-y-12 px-4 py-12 sm:px-6 sm:py-16 lg:px-8"> <!-- Top Grid: Brand & Categorized Columns --> <div class="grid grid-cols-2 gap-8 text-left md:grid-cols-6 lg:grid-cols-12"> <!-- Brand Info (4 Cols) --> <div class="col-span-2 space-y-4 md:col-span-6 lg:col-span-4"> <div class="flex items-center gap-2 font-mono text-base font-bold tracking-tight"> <span class="bg-primary text-primary-foreground flex size-6 items-center justify-center rounded-lg text-xs font-bold shadow-xs" > U </span> <span>UIPKGE.DEV</span> </div> <p class="text-muted-foreground max-w-sm text-xs leading-relaxed"> The open-source unbundled component registry where you own 100% of the source code. Calibrated tokens, pure AST distribution, and mathematical dual-framework parity across Vue 3.5 and React 19. </p> <!-- Social links --> <div class="flex items-center gap-2 pt-2"> <button ui-button size="sm" variant="outline" class="size-8 rounded-lg p-0" aria-label="GitHub"> <lucide-icon [img]="Github" class="size-3.5" /> </button> <button ui-button size="sm" variant="outline" class="size-8 rounded-lg p-0" aria-label="Twitter"> <lucide-icon [img]="Twitter" class="size-3.5" /> </button> <button ui-button size="sm" variant="outline" class="size-8 rounded-lg p-0" aria-label="Website"> <lucide-icon [img]="Globe" class="size-3.5" /> </button> </div> </div> <!-- 4 Links Columns (8 Cols total, 2 cols each) --> @for (col of columnList; track col.title) { <div class="col-span-1 space-y-3 md:col-span-3 lg:col-span-2"> <h4 class="text-foreground font-mono text-xs font-bold tracking-wider uppercase"> {{ col.title }} </h4> <ul class="text-muted-foreground space-y-2 font-mono text-xs"> @for (link of col.links; track link.label) { <li> <a [href]="link.href" class="hover:text-foreground inline-flex items-center gap-1.5 transition-colors" > <span>{{ link.label }}</span> @if (link.badge) { <span class="py-0.2 bg-primary/20 text-primary rounded px-1.5 text-xs font-bold uppercase"> {{ link.badge }} </span> } </a> </li> } </ul> </div> } </div> <!-- Bottom Operational Status & Copyright Bar --> <div class="border-border/80 text-muted-foreground flex flex-col items-center justify-between gap-4 border-t pt-8 text-center font-mono text-xs sm:flex-row sm:text-left" > <!-- Live System Telemetry Status --> <div class="flex items-center gap-2.5"> <span class="relative flex size-2"> <span class="bg-success absolute inline-flex h-full w-full rounded-full opacity-75"></span> <span class="bg-success relative inline-flex size-2 rounded-full"></span> </span> <span class="text-foreground font-medium">All Global Edge Nodes Operational</span> <span class="text-muted-foreground/60">• Latency: 14ms (P99)</span> </div> <!-- Right Copyright & MIT Notice --> <div class="flex items-center gap-3"> <span>MIT Licensed • Unbundled Code</span> <span>© {{ currentYear }} UIPKGE</span> </div> </div> </div> </footer> `, }) export class UiFooterDeveloperDenseGridComponent { protected readonly Github = Github protected readonly Globe = Globe protected readonly Twitter = Twitter protected readonly columnList = developerColumns protected readonly currentYear = new Date().getFullYear() @Input('class') className?: string get rootClass(): string { return cn('border-border bg-card text-foreground border-t', this.className) } } // --------------------------------------------------------------------------- // Footer — Mega with Newsletter: signup + five link columns + social row. // --------------------------------------------------------------------------- interface MegaSection { heading: string links: string[] } const megaSections: MegaSection[] = [ { heading: 'Product', links: ['Metric layer', 'Dashboards', 'Alerting', 'Embedding', 'Changelog'] }, { heading: 'Solutions', links: ['Finance', 'RevOps', 'Analytics', 'Engineering'] }, { heading: 'Developers', links: ['Documentation', 'API reference', 'CLI', 'Status'] }, { heading: 'Company', links: ['About', 'Careers', 'Customers', 'Press kit'] }, { heading: 'Legal', links: ['Privacy', 'Terms', 'DPA', 'Subprocessors'] }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-mega-newsletter, [ui-footer-mega-newsletter]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiInputComponent, UiSeparatorComponent], template: ` <footer data-slot="footer-mega-newsletter" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-14"> <div class="grid gap-12 lg:grid-cols-[22rem_1fr] lg:gap-16"> <div> <a href="#top" class="flex items-center gap-2 font-semibold tracking-tight"> <lucide-icon [img]="Layers" class="text-primary size-5" aria-hidden="true" /> Northwind </a> <p class="text-muted-foreground mt-3 max-w-sm text-sm leading-relaxed"> One certified metric layer between your warehouse and everything downstream. </p> <form class="mt-6" (submit)="subscribe($event)"> <label for="footer-email" class="text-sm font-medium">Monthly engineering notes</label> <div class="mt-2 flex gap-2"> <ui-input id="footer-email" [value]="email()" (valueChange)="email.set($event)" type="email" placeholder="[email protected]" autoComplete="email" class="max-w-64" /> <button ui-button type="submit"> Subscribe <lucide-icon [img]="ArrowRight" class="ml-1.5 size-3.5" aria-hidden="true" /> </button> </div> <!-- Status line reserves its own row, so confirming does not shift the columns. --> <p class="text-muted-foreground mt-2 min-h-5 text-xs"> @if (submitted()) { <span class="text-success inline-flex items-center gap-1.5"> <lucide-icon [img]="Check" class="size-3" aria-hidden="true" /> Check your inbox to confirm. </span> } @else { <span>One email a month. Unsubscribe in one click.</span> } </p> </form> </div> <div class="grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-5"> @for (section of sectionList; track section.heading) { <div> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase"> {{ section.heading }} </p> <ul class="mt-3 space-y-2"> @for (link of section.links; track link) { <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> {{ link }} </a> </li> } </ul> </div> } </div> </div> <div ui-separator class="my-10"></div> <div class="flex flex-col gap-4 sm:flex-row sm:items-center"> <p class="text-muted-foreground text-xs">© 2026 Northwind Data, Inc. All rights reserved.</p> <span ui-badge variant="outline" class="w-fit">SOC 2 Type II</span> <div class="flex items-center gap-1 sm:ml-auto"> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <lucide-icon [img]="Github" class="size-4" aria-hidden="true" /> </button> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <lucide-icon [img]="Linkedin" class="size-4" aria-hidden="true" /> </button> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="YouTube"> <lucide-icon [img]="Youtube" class="size-4" aria-hidden="true" /> </button> </div> </div> </div> </footer> `, }) export class UiFooterMegaNewsletterComponent { protected readonly ArrowRight = ArrowRight protected readonly Check = Check protected readonly Github = Github protected readonly Layers = Layers protected readonly Linkedin = Linkedin protected readonly Youtube = Youtube protected readonly sectionList = megaSections @Input('class') className?: string readonly email = signal('') readonly submitted = signal(false) get rootClass(): string { return cn('border-border bg-background border-t', this.className) } subscribe(event?: Event): void { event?.preventDefault() if (!this.email().trim()) return this.submitted.set(true) } } // --------------------------------------------------------------------------- // Footer — Minimal Row: wordmark, inline links, social icons, copyright. // --------------------------------------------------------------------------- const minimalLinks = [ { label: 'Product', href: '#product' }, { label: 'Pricing', href: '#pricing' }, { label: 'Docs', href: '#docs' }, { label: 'Privacy', href: '#privacy' }, { label: 'Terms', href: '#terms' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-minimal-row, [ui-footer-minimal-row]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiButtonComponent, UiSeparatorComponent], template: ` <footer data-slot="footer-minimal-row" [class]="rootClass"> <div class="mx-auto flex max-w-6xl flex-col gap-4 px-6 py-6 sm:flex-row sm:items-center"> <a href="#top" class="flex shrink-0 items-center gap-2 text-sm font-semibold tracking-tight"> <lucide-icon [img]="Layers" class="text-primary size-4" aria-hidden="true" /> Northwind </a> <div ui-separator orientation="vertical" class="hidden h-4 sm:block"></div> <nav class="flex flex-wrap items-center gap-x-5 gap-y-2" aria-label="Footer"> @for (link of linkList; track link.href) { <a [href]="link.href" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> {{ link.label }} </a> } </nav> <div class="flex items-center gap-1 sm:ml-auto"> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <lucide-icon [img]="Github" class="size-4" aria-hidden="true" /> </button> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <lucide-icon [img]="Linkedin" class="size-4" aria-hidden="true" /> </button> <span class="text-muted-foreground ml-2 text-xs">© 2026 Northwind</span> </div> </div> </footer> `, }) export class UiFooterMinimalRowComponent { protected readonly Github = Github protected readonly Layers = Layers protected readonly Linkedin = Linkedin protected readonly linkList = minimalLinks @Input('class') className?: string get rootClass(): string { return cn('border-border bg-background border-t', this.className) } } // --------------------------------------------------------------------------- // Footer — Dense Sitemap: six columns, region/currency selects, status row. // --------------------------------------------------------------------------- interface SitemapSection { heading: string links: string[] } const sitemapSections: SitemapSection[] = [ { heading: 'Platform', links: ['Metric layer', 'Dashboards', 'Alerting', 'Embedding', 'API', 'CLI'] }, { heading: 'Solutions', links: ['Finance', 'RevOps', 'Analytics', 'Engineering', 'Healthcare', 'Retail'] }, { heading: 'Developers', links: ['Documentation', 'API reference', 'SDKs', 'Webhooks', 'Status', 'Changelog'], }, { heading: 'Resources', links: ['Guides', 'Benchmarks', 'Customers', 'Blog', 'Webinars', 'Glossary'] }, { heading: 'Company', links: ['About', 'Careers', 'Press kit', 'Partners', 'Contact', 'Security'] }, { heading: 'Legal', links: ['Privacy', 'Terms', 'DPA', 'Subprocessors', 'SLA', 'Accessibility'] }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-footer-sitemap-dense, [ui-footer-sitemap-dense]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, UiSeparatorComponent, ], template: ` <footer data-slot="footer-sitemap-dense" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-12"> <div class="grid grid-cols-2 gap-x-6 gap-y-8 sm:grid-cols-3 lg:grid-cols-6"> @for (section of sectionList; track section.heading) { <div> <p class="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase"> {{ section.heading }} </p> <ul class="mt-3 space-y-1.5"> @for (link of section.links; track link) { <li> <a href="#" class="text-muted-foreground hover:text-foreground text-sm transition-colors"> {{ link }} </a> </li> } </ul> </div> } </div> <div ui-separator class="my-10"></div> <div class="flex flex-col gap-6 lg:flex-row lg:items-center"> <a href="#top" class="flex shrink-0 items-center gap-2 text-sm font-semibold tracking-tight"> <lucide-icon [img]="Layers" class="text-primary size-4" aria-hidden="true" /> Northwind </a> <div class="flex flex-wrap items-center gap-2"> <ui-select [value]="region()" (valueChange)="region.set($event)"> <button ui-select-trigger class="h-8 w-40 text-xs" aria-label="Data region"> <ui-select-value /> </button> <ui-select-content> <ui-select-item value="eu">EU (Frankfurt)</ui-select-item> <ui-select-item value="us">US (Virginia)</ui-select-item> <ui-select-item value="au">AU (Sydney)</ui-select-item> </ui-select-content> </ui-select> <ui-select [value]="currency()" (valueChange)="currency.set($event)"> <button ui-select-trigger class="h-8 w-28 text-xs" aria-label="Currency"> <ui-select-value /> </button> <ui-select-content> <ui-select-item value="eur">EUR €</ui-select-item> <ui-select-item value="usd">USD $</ui-select-item> <ui-select-item value="gbp">GBP £</ui-select-item> </ui-select-content> </ui-select> </div> <div class="flex flex-wrap items-center gap-3 lg:ml-auto"> <!-- Status dot is a live indicator, so it carries its own text for screen readers rather than relying on colour alone. --> <a href="#status" class="hover:bg-muted flex items-center gap-2 rounded-md px-2 py-1 transition-colors"> <span class="bg-success size-2 rounded-full" aria-hidden="true"></span> <span class="text-muted-foreground text-xs">All systems operational</span> </a> <span ui-badge variant="outline">SOC 2</span> <span ui-badge variant="outline">GDPR</span> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="GitHub"> <lucide-icon [img]="Github" class="size-4" aria-hidden="true" /> </button> <button ui-button variant="ghost" size="icon" class="size-8" aria-label="LinkedIn"> <lucide-icon [img]="Linkedin" class="size-4" aria-hidden="true" /> </button> </div> </div> <p class="text-muted-foreground mt-6 text-xs">© 2026 Northwind Data, Inc. All rights reserved.</p> </div> </footer> `, }) export class UiFooterSitemapDenseComponent { protected readonly Github = Github protected readonly Layers = Layers protected readonly Linkedin = Linkedin protected readonly sectionList = sitemapSections @Input('class') className?: string readonly region = signal('eu') readonly currency = signal('eur') get rootClass(): string { return cn('border-border bg-background border-t', this.className) } }src/app/components/blocks/footer/index.ts0.3 kB
export { UiFooter01Component, UiFooterBrandStatementComponent, UiFooterDeveloperDenseGridComponent, UiFooterMegaNewsletterComponent, UiFooterMinimalRowComponent, UiFooterSitemapDenseComponent, type DeveloperFooterColumn, } from './footer.component'
Raw manifest:https://uipkge.dev/r/angular/footer.json