npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@meetreeve/consent-kit

v0.1.2

Published

Cookie-consent banner for Reeve host apps — vanilla-cookieconsent engine wrapped in a themeable React primitive, with GDPR/CCPA presets and Reeve pixel/GA4/Meta/PostHog autoBlock wiring.

Readme

@meetreeve/consent-kit

Cookie-consent banner for Reeve host apps — a themeable React primitive wrapping vanilla-cookieconsent (MIT, orestbida), with GDPR/CCPA presets and Reeve's pixel/GA4/Meta/PostHog autoBlock wiring.

Ships one component: <ConsentBanner>. It renders in the host page's own DOM (not an iframe — see packages/embed/src/surfaces/, which is a deliberately different pattern for remote-app embeds) because a cookie banner has to gate <script> tags that live on the parent page; an iframe has no authority over that.

Scope

This package builds the popup + engine + theming + autoBlock config. It does not:

  • Call the consent ledger API. onConsent is a pure callback — the browser can't hold the HMAC secret the ledger endpoint requires. Wiring a BFF proxy to POST /api/compliance/v1/consent is DEV-4356.
  • Detect geography (GDPR vs CCPA). region is caller-supplied. Deriving it from x-vercel-ip-country is also DEV-4356.
  • Mount itself into reeve-frontend or gate tracking-slots.tsx. Also DEV-4356.
  • Seed the legal_documents doc_type="cookie" row the ledger needs to validate against — prod currently has zero rows of that type; that's a content/ops prerequisite tracked on DEV-4356, not this package.

Install

pnpm add @meetreeve/consent-kit

react / react-dom (^18 || ^19) are peer dependencies. vanilla-cookieconsent and @meetreeve/embed are regular dependencies (installed automatically).

Quick start

import { ConsentBanner, toLedgerPayload } from '@meetreeve/consent-kit';
import '@meetreeve/consent-kit/styles/cookieconsent.css';

export function CookieConsentGate({ brand }: { brand: HostAppBrand }) {
  return (
    <ConsentBanner
      hostApp="acme-tenant"
      brand={brand}
      region="gdpr" // or "ccpa" / "none" — geo-resolution is the caller's job (DEV-4356)
      onConsent={(categories, action) => {
        // Pure callback — no network call happens here. DEV-4356's BFF
        // proxy takes this and POSTs it to the consent ledger.
        const payload = toLedgerPayload(categories, action);
        // fetch('/api/consent', { method: 'POST', body: JSON.stringify(payload) })
      }}
    />
  );
}

region="none" renders nothing and never touches the underlying engine — useful while a host app hasn't wired geo detection yet.

Category model

| Category | Always on? | Gates (via tracking-slots.tsx, DEV-3322) | |---|---|---| | necessary | Yes — readOnly, not user-togglable | n/a | | analytics | No | PostHog | | marketing | No | Meta Pixel, GA4, Reeve's first-party pixel loader (adbot/static/pixel/r.js) |

The full service→category map lives in REEVE_AUTOBLOCK_SERVICES (exported from the package root) — treat the pixel-loader bucketing as a reviewable default, not dogma.

GDPR vs CCPA presets

| | region="gdpr" | region="ccpa" | |---|---|---| | Mode | opt-in | opt-out | | Default state | analytics/marketing start rejected | analytics/marketing start accepted | | Primary framing | "We use cookies" / Accept all / Reject all | "Your privacy choices" / Do Not Sell or Share My Personal Information | | Visitor action | Must affirmatively opt in | May opt out ("Do Not Sell") |

Both presets share the same category model and autoBlock wiring — only mode, default category.enabled, and modal copy differ. See src/presets/gdpr.ts / src/presets/ccpa.ts.

autoBlock (script-tag gating)

vanilla-cookieconsent v3 implements what v2 called "autoBlock" via manageScriptTags (on by default in this package's engine config) plus a data-category/data-service convention. Whoever injects a tracking script (DEV-4356's mount, today) tags it:

<script type="text/plain" data-category="marketing" data-service="ga4">
  // GA4 bootstrap
</script>

Until the visitor accepts the marketing category (and, if declared, the ga4 service specifically), the script stays inert (type="text/plain" isn't a runnable MIME type). The moment consent is granted, vanilla-cookieconsent clones the tag without the blocking type attribute and swaps it into the DOM, making it execute. This is a document-wide scan — the tagged <script> doesn't need to live inside <ConsentBanner>'s own container.

Theming contract

<ConsentBanner brand={brand} /> re-skins the underlying engine by writing --cc-* CSS custom properties (vanilla-cookieconsent's own variable contract — see its dist/css-components/*.css) as inline styles on the banner's container element, so they cascade only to that instance.

brandToConsentVars(brand: HostAppBrand) (also exported standalone) does the mapping. It is a separate function from @meetreeve/embed/brand's brandToCssVars() — that helper targets Documenso's CssVars naming (--primary, --accent, …); vanilla-cookieconsent has its own prefixed names, so this package maps HostAppBrand directly rather than piping through brandToCssVars's output.

| HostAppBrand field | --cc-* variable | |---|---| | primary_color | --cc-btn-primary-bg, --cc-btn-primary-border-color, --cc-toggle-on-bg | | primary_foreground | --cc-btn-primary-color | | accent_color | --cc-btn-primary-hover-bg, --cc-link-color (falls back to primary_color) | | background_color | --cc-bg (omitted if null) | | muted_color | --cc-btn-secondary-bg, --cc-cookie-category-block-bg (omitted if null) | | muted_foreground | --cc-btn-secondary-color, --cc-secondary-color (omitted if null) | | border_color | --cc-separator-border-color (omitted if null) | | radius | --cc-modal-border-radius, --cc-btn-border-radius, --cc-pm-toggle-border-radius | | font_family_sans | --cc-font-family (omitted if null) | | extra_css_vars | Any key already prefixed --cc- wins over the mapped defaults (same escape-hatch precedence as brandToCssVars) |

You must still import the base stylesheet once (it supplies layout, spacing, and the light/dark scheme defaults the --cc-* variables plug into):

import '@meetreeve/consent-kit/styles/cookieconsent.css';

This is vanilla-cookieconsent's own CSS, copied into this package's build output — no CDN fetch, no external font @import. Self-contained.

onConsent → ledger payload contract

onConsent(categories, action) fires whenever the visitor's consent state is (re)confirmed (initial accept/reject, or a saved preferences-panel change). toLedgerPayload() shapes that into what DEV-4356's BFF proxy should POST to the DEV-2614 consent ledger (/api/compliance/v1/consent, reeve-services):

interface ConsentLedgerPayload {
  doc_type: 'cookie';       // already a first-class value — api/models/compliance.py:37
  subject_type: 'visitor';  // anonymous visitor, not an authenticated user/tenant
  action: 'all' | 'custom' | 'necessary';
  categories: ('necessary' | 'analytics' | 'marketing')[];
  source: 'cookie_banner';
}

action mirrors vanilla-cookieconsent's own AcceptType vocabulary (all / custom / necessary) rather than inventing a parallel one:

  • 'all' — visitor clicked Accept all.
  • 'necessary' — visitor clicked Reject all / Do Not Sell (only the always-on category applies).
  • 'custom' — visitor saved a specific subset via "Manage preferences".

This package never constructs the HTTP request — the ledger write is HMAC-signed server-to-server, and a browser can't hold that secret.

API

<ConsentBanner> (@meetreeve/consent-kit/primitives, also re-exported from the root)

| Prop | Type | Notes | |---|---|---| | hostApp | string | Debug/e2e marker only (data-consent-kit-host). Never transmitted. | | brand | HostAppBrand | From @meetreeve/embed/brand. Re-skins via --cc-* vars. | | region | 'gdpr' \| 'ccpa' \| 'none' | 'none' renders nothing. | | onConsent? | (categories: ConsentCategory[], action: ConsentAction) => void | Pure callback; safe to pass a new inline function each render — identity changes don't remount the engine. | | policyLinks? | { cookiePolicyHref?: string; privacyPolicyHref?: string } | Caller-supplied links to the host app's own policy documents (DEV-7210), rendered as plain anchors in the consent modal's footer in every region. Caller-supplied because host apps live on different domains. Omit for no footer at all. | | layout? | CookieConsent.ConsentModalLayout | Overrides guiOptions.consentModal.layout (DEV-9938). Omit to use the region's own default — see "Narrow-viewport GDPR default" below. | | position? | CookieConsent.ConsentModalPosition | Overrides guiOptions.consentModal.position (DEV-9938). Same override relationship as layout. | | className? | string | Applied to the container <div>. |

Narrow-viewport GDPR default (DEV-9938)

gdprPreset ships layout: 'box', position: 'bottom right' — a floating card anchored to the bottom-right corner. At narrow viewports (at or under a 640px width, checked once at mount via window.matchMedia) that card measured tall enough to occlude a template's hero heading/subhead, so region="gdpr" auto-switches to a shorter, full-width bottom bar instead unless the caller passes an explicit layout and/or position — an explicit prop always wins, at any viewport width. region="ccpa" is unaffected; its preset already ships a bar layout.

This default is a bundle of three things, all gated on the same region/width/no-explicit-prop condition:

  1. layout: 'bar', position: 'bottom' instead of 'box'/'bottom right'.
  2. A shorter consentModal.description (same substance, fewer words — "Manage preferences" still shows the full sentence).
  3. A max-height: 230px cap on the modal itself (with internal scroll), shipped as part of @meetreeve/consent-kit/styles/cookieconsent.css — no extra import needed.

At >640px (or with an explicit layout/position prop), none of this applies — the banner renders exactly as it did before this default existed.

Presets (@meetreeve/consent-kit/presets)

gdprPreset, ccpaPresetConsentPreset objects (mode, guiOptions, translation) consumed internally by <ConsentBanner>. Exported for callers building a custom engine config.

Other root exports

  • toLedgerPayload(categories, action) => ConsentLedgerPayload — pure data shaper, see above.
  • REEVE_AUTOBLOCK_SERVICES — the service → category map used to populate each category's services list in the preferences panel.
  • brandToConsentVars(brand) => Record<string, string> — the theming mapping, exposed standalone for advanced integrations.

Deviations from the vendored engine's defaults

  • hideFromBots: false (library default: true). The library's bot heuristic checks navigator.webdriver / a UA regex and silently skips rendering when it thinks it's a crawler — which makes browser-automation test/CI environments non-deterministic. Reeve already gates search-engine indexing elsewhere; disabling this here trades a marginal SEO nicety for predictable behavior everywhere the banner runs.
  • onConsent/onChange deduping: the vendored engine can fire both its own onConsent and onChange hooks for a single user action in opt-out mode. This package's <ConsentBanner onConsent> dedupes by comparing the resulting (categories, action) tuple against the last emission, so callers always get exactly one call per distinct outcome.

License

This package: UNLICENSED (internal, mirrors @meetreeve/chat-kit/@meetreeve/embed).

Vendored dependency vanilla-cookieconsent (orestbida): MIT. See node_modules/vanilla-cookieconsent/LICENSE after install, or upstream.