Cookie Consent — Bar
blockmarketingBottom consent bar offering accept, reject, and manage as equally weighted actions, with a one-line statement of what is actually stored.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/lit/cookie-consent-bar.json$npx shadcn@latest add https://uipkge.dev/r/lit/cookie-consent-bar.json$yarn dlx shadcn@latest add https://uipkge.dev/r/lit/cookie-consent-bar.json$bunx shadcn@latest add https://uipkge.dev/r/lit/cookie-consent-bar.jsonManifest:
https://uipkge.dev/r/lit/cookie-consent-bar.jsonInstalls to:src/uip/blocks/cookie-consent-bar/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
storage-key | string | uipkge:consent | optional |
force-visible | boolean | false | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
HTMLElementTagNameMapinterface HTMLElementTagNameMap {
'uip-cookie-consent-bar': UipCookieConsentBar
}
}Files installed (1)
src/uip/blocks/cookie-consent-bar/cookie-consent-bar.ts4.3 kB
import { LitElement, css, html, nothing } from 'lit' import { Cookie } from 'lucide' import { tailwind } from '../../lib/styles' import { ThemeController } from '../../lib/theme' import { icon } from '../../lib/icon' // Side-effect imports: register the composed elements for standalone use // (consumers who install only this block still get working primitives). import '../../components/badge/badge' import '../../components/button/button' /** * <uip-cookie-consent-bar> — the registry CookieConsentBar block as a web component. * * Class strings are React's CookieConsentBar verbatim. Visibility reads * `storage-key` from localStorage in `connectedCallback` (browser-only, like * React's useEffect) and persists the choice there. `force-visible` is a * demo affordance that skips the storage check so the bar always renders; * `reset()` clears the stored choice and shows the bar again. */ export class UipCookieConsentBar extends LitElement { static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true } // :host display can't be a utility class (the host has no template of its own). static styles = [tailwind, css`:host { display: block; }`] static properties = { storageKey: { attribute: 'storage-key' }, forceVisible: { type: Boolean, attribute: 'force-visible' }, visible: { state: true }, } storageKey = 'uipkge:consent' forceVisible = false private visible = false constructor() { super() new ThemeController(this) } connectedCallback() { super.connectedCallback() this.setAttribute('data-uipkge', '') this.setAttribute('data-slot', 'cookie-consent-bar') if (this.forceVisible) { this.visible = true return } try { this.visible = !window.localStorage.getItem(this.storageKey) } catch { this.visible = true } } /** Clears the stored choice and shows the bar again (demo affordance). */ reset() { try { window.localStorage.removeItem(this.storageKey) } catch { // Blocked storage: the choice holds for this page view only. } this.visible = true } private decide(choice: 'accepted' | 'rejected') { this.visible = false try { window.localStorage.setItem(this.storageKey, choice) } catch { // Blocked storage: the choice holds for this page view only. } } render() { if (!this.visible) return nothing return html` <div data-slot="cookie-consent-bar" class="border-border bg-card/95 fixed inset-x-0 bottom-0 z-50 border-t backdrop-blur" role="region" aria-label="Cookie consent" > <div class="mx-auto flex max-w-6xl flex-col gap-4 px-6 py-4 lg:flex-row lg:items-center lg:gap-8"> <div class="flex min-w-0 items-start gap-3"> ${icon(Cookie, 'cookie', 'text-muted-foreground mt-0.5 size-4 shrink-0')} <div class="min-w-0"> <div class="flex flex-wrap items-center gap-2"> <p class="text-sm font-medium">We store two things</p> <uip-badge variant="outline">No ad tracking</uip-badge> </div> <p class="text-muted-foreground mt-1 text-sm leading-relaxed"> A session cookie so you stay signed in, and an analytics cookie recording which pages get read. Rejecting keeps the first and drops the second. </p> </div> </div> <div data-uipkge="" data-slot="separator" data-orientation="vertical" role="none" class="bg-border shrink-0 w-px hidden h-10 lg:block" ></div> <div class="flex shrink-0 flex-wrap items-center gap-2"> <uip-button variant="outline" @click=${() => this.decide('rejected')} > Reject analytics </uip-button> <uip-button @click=${() => this.decide('accepted')} > Accept </uip-button> <uip-button variant="ghost" size="sm">Manage</uip-button> </div> </div> </div> ` } } customElements.get('uip-cookie-consent-bar') || customElements.define('uip-cookie-consent-bar', UipCookieConsentBar) declare global { interface HTMLElementTagNameMap { 'uip-cookie-consent-bar': UipCookieConsentBar } }
Raw manifest:https://uipkge.dev/r/lit/cookie-consent-bar.json