Cookie Consent Banner
blocklegalGDPR-style cookie consent banner with two variants: a full-width bottom bar and a compact card. Customize expands inline preferences with Necessary (always on), Analytics, and Marketing switches. Accept, Reject, and Save dismiss with a 150ms fade.
Installation
$pnpm dlx uipkge-ng@latest add cookie-consent-banner$npx uipkge-ng@latest add cookie-consent-banner$yarn dlx uipkge-ng@latest add cookie-consent-banner$bunx uipkge-ng@latest add cookie-consent-bannerManifest:
https://uipkge.dev/r/angular/cookie-consent-banner.jsonInstalls to:src/app/components/blocks/cookie-consent-banner/Variants
Loading interactive previews…
Loading interactive previews…
npm dependencies
Files installed (2)
src/app/components/blocks/cookie-consent-banner/cookie-consent-banner.component.ts8.3 kB
// GDPR-style cookie consent banner with two variants: a full-width bottom bar and a compact // card. Customize expands inline preferences with Necessary (always on), Analytics, and // Marketing switches. Accept, Reject, and Save dismiss with a 150ms fade. // Port of the Vue/React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { Cookie, LucideAngularModule } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiButtonComponent } from '@/ui/button/button.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiSwitchComponent } from '@/ui/switch/switch.component' export type CookieConsentBannerVariant = 'bottom-bar' | 'card' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-cookie-consent-banner, [ui-cookie-consent-banner]', standalone: true, // The block renders its own root <div>: keep this wrapper out of the layout. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiButtonComponent, UiSeparatorComponent, UiSwitchComponent], template: ` @if (visible()) { <div data-slot="cookie-consent-banner" role="dialog" aria-label="Cookie consent" [class]="rootClass"> @if (variant === 'bottom-bar') { <div class="bg-card border-border w-full border-t px-4 py-4 sm:px-6"> <div class="mx-auto flex max-w-5xl flex-col gap-4"> <div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between"> <div class="flex items-start gap-3"> <span class="bg-primary/10 text-primary mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full" > <lucide-icon [img]="Cookie" class="size-4" aria-hidden="true" /> </span> <p class="text-muted-foreground max-w-xl text-sm leading-relaxed"> We use cookies to improve your experience and analyze traffic. Read our <a href="#" class="text-foreground focus-visible:ring-ring rounded-sm font-medium underline underline-offset-4 outline-none focus-visible:ring-2" >Cookie Policy</a >. </p> </div> <div class="flex shrink-0 flex-wrap items-center gap-2"> <button ui-button variant="ghost" size="sm" (click)="showPreferences.set(!showPreferences())"> {{ showPreferences() ? 'Hide preferences' : 'Customize' }} </button> <button ui-button variant="outline" size="sm" (click)="dismiss()">Reject</button> <button ui-button size="sm" (click)="dismiss()">Accept all</button> </div> </div> @if (showPreferences()) { <div class="flex flex-col gap-3"> <ui-separator /> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Necessary</p> <p class="text-muted-foreground text-xs">Required for the site to function.</p> </div> <ui-switch [checked]="true" disabled aria-label="Necessary cookies" /> </div> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Analytics</p> <p class="text-muted-foreground text-xs">Helps us understand usage patterns.</p> </div> <ui-switch [checked]="analytics()" (checkedChange)="analytics.set($event)" aria-label="Analytics cookies" /> </div> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Marketing</p> <p class="text-muted-foreground text-xs">Used to personalize campaigns.</p> </div> <ui-switch [checked]="marketing()" (checkedChange)="marketing.set($event)" aria-label="Marketing cookies" /> </div> <div class="mt-1 flex justify-end"> <button ui-button size="sm" (click)="dismiss()">Save preferences</button> </div> </div> } </div> </div> } @else { <div class="bg-card border-border mx-auto max-w-md rounded-xl border p-6 shadow-xs"> <div class="flex flex-col gap-4"> <div class="flex items-start gap-3"> <span class="bg-primary/10 text-primary flex size-9 shrink-0 items-center justify-center rounded-full"> <lucide-icon [img]="Cookie" class="size-4" aria-hidden="true" /> </span> <div> <h3 class="text-sm font-semibold">We value your privacy</h3> <p class="text-muted-foreground mt-1 text-sm leading-relaxed"> We use cookies to improve your experience and analyze traffic. Read our <a href="#" class="text-foreground focus-visible:ring-ring rounded-sm font-medium underline underline-offset-4 outline-none focus-visible:ring-2" >Cookie Policy</a >. </p> </div> </div> @if (showPreferences()) { <ui-separator /> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Necessary</p> <p class="text-muted-foreground text-xs">Required for the site to function.</p> </div> <ui-switch [checked]="true" disabled aria-label="Necessary cookies" /> </div> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Analytics</p> <p class="text-muted-foreground text-xs">Helps us understand usage patterns.</p> </div> <ui-switch [checked]="analytics()" (checkedChange)="analytics.set($event)" aria-label="Analytics cookies" /> </div> <div class="flex items-center justify-between gap-4 py-1"> <div> <p class="text-sm font-medium">Marketing</p> <p class="text-muted-foreground text-xs">Used to personalize campaigns.</p> </div> <ui-switch [checked]="marketing()" (checkedChange)="marketing.set($event)" aria-label="Marketing cookies" /> </div> } <div class="flex flex-wrap items-center gap-2"> <button ui-button variant="ghost" size="sm" (click)="showPreferences.set(!showPreferences())"> {{ showPreferences() ? 'Hide preferences' : 'Customize' }} </button> <div class="ml-auto flex items-center gap-2"> <button ui-button variant="outline" size="sm" (click)="dismiss()">Reject</button> <button ui-button size="sm" (click)="dismiss()"> {{ showPreferences() ? 'Save preferences' : 'Accept all' }} </button> </div> </div> </div> </div> } </div> } `, }) export class UiCookieConsentBannerComponent { protected readonly Cookie = Cookie @Input() variant: CookieConsentBannerVariant = 'bottom-bar' @Input('class') className?: string readonly visible = signal(true) readonly showPreferences = signal(false) readonly analytics = signal(true) readonly marketing = signal(false) get rootClass(): string { return cn('animate-in fade-in duration-150', this.className) } dismiss(): void { this.visible.set(false) } }src/app/components/blocks/cookie-consent-banner/index.ts0.1 kB
export { UiCookieConsentBannerComponent, type CookieConsentBannerVariant } from './cookie-consent-banner.component'
Raw manifest:https://uipkge.dev/r/angular/cookie-consent-banner.json