consentpro-v2
2.5.1
Patch Changes
fecdc78: Drive the custom-site "Powered by Consent Pro" mark from the project's own config.
The mark was decided entirely by plan tier: the served script inlined
window.fs_consent_pro_attribution = truefor free-tier sites and nothing else ever set it, so a paid custom site never showed it and the app's "Hide Consent Pro logo" toggle changed nothing outside the in-app preview. Preview links were worse —applyStoredreassigns the global from the fetched payload, which never carriesattribution, so a preview dropped the mark even on the free tier.consentpro-types
ConsentProV2RuntimeConfigurationgainshideBranding, carried through to the sanitized client config.
server
projectConfigToRuntimemapsbanner_settings.hide_brandingonto it, so it ships with every publish and with the preview config.
consentpro-v2
- Custom sites resolve
showAttributionfrom that config, defaulting to showing the mark. The free tier's license-derived global still wins, so a free site cannot switch it off.
9a490af: Pick the "Powered by Consent Pro" logo colour from the banner background.
consentpro-utils
- New
relativeLuminanceandcontrastingMonochromehelpers: WCAG 2.1 relative luminance, and a black/white pick by contrast ratio against a background.
web-components
ConsentBanner's wordmark now reads--consentpro-color-attribution, falling back to the banner's own text colour. The wordmark is alreadycurrentColor, so the variable flips it; the "Powered by" label keeps the banner's text colour.
consentpro-v2 / web-app
- Both set
--consentpro-color-attributionfrom the banner background, so the live banner and the style preview render the same logo.
- New
fecdc78: Fix the custom-site opt-in banner rendering without its reject button.
The button set only ever reached the runtime through the CDN copy fetch, so any failed fetch left
<consentpro-banner>on its own defaults, which show preferences and accept but defaultshowDenyButtontofalse. An opt-in banner rendered accept-only, silently, which is a consent-compliance failure rather than a cosmetic one. One way to trigger it:instanceConfigwrites theconsentpro-modequery param intobannerType, but that param can carry an instance key rather than a banner type (the debugger's keys are region-derived), and the copy request forus-or400s.consentpro-dictionary
BANNER_STRUCTUREand itsBannerStructuretype move to their own module and are exported fromconsentpro-dictionary/consent-copy. The record is unchanged; it was already locale-independent, so nothing about the served copy changes.- New
getBannerStructure(method)resolves an unvalidated banner type, falling back to theopt-instructure — the only one that always offers a deny button — rather than returningundefined.
consentpro-v2
- The custom-site banner resolves its button structure locally from
bannerTypeand layers the fetched copy's strings on top, so the button set no longer depends on the network.showGpcNoticeis now derived from the same local structure gated on the visitor's GPC signal, instead of from the fetched copy. instanceConfig's handling ofconsentpro-modeis left alone deliberately: it is shared with Webflow sites, and the local structure fallback already keeps the banner compliant for the unvalidated types it produces.- A failed copy fetch warns to the console instead of being swallowed, so a banner falling back to English defaults is diagnosable.
8384efa: Show a placeholder overlay on iframes blocked for want of consent on custom (non-Webflow) sites.
Custom sites have no pasted
fs-consent-element="placeholder"markup, so a blocked embed simply lost itssrcand rendered as a blank box. The overlay now ships as a web component thatcore/v2swaps into the blocked iframe itself.web-components
- New
<consentpro-placeholder>custom element, built as its ownplaceholder.jsCDN bundle. Like every other component in this package, its props are plain JS properties observed as no attribute, and its buttons dispatch bubbling, composedconsentpro:open-preferences/consentpro:allowevents.
consentpro-dictionary
getConsentCopyreturns aplaceholderblock (message,preferencesText,acceptAllText), so the overlay is localized like every other component.
server
GET /v2/cdn/web-components/placeholder.jsserves the new bundle, andcopy.jsoncarries theplaceholderstrings.
consentpro-v2
new/custom-site/placeholder.tsbuilds the blocked iframe's document as a string, which the existing blob path swaps into the iframe'ssrc. It is self-contained: the CDN module that registers the element, plus the script that waits for that registration, creates the element and assigns the copy as JS properties. No markup is parsed, mutated or serialized on the way, and the copy is escaped into the script rather than interpolated into HTML.- That script tag is pinned with the build-time
sha384hash, soplaceholder.jsis now SRI-checked like the other component bundles. - Blocked iframes wire the overlay's events through
custom-site/events.ts: the document is a same-originblob:, so the component'sconsentpro:*events reach the consent-state layer directly. - A blocked iframe's own
sandboxis stashed and dropped while the placeholder is shown, then restored verbatim before the original URL loads again. It applies to the untrusted embed, not to our placeholder, and withoutallow-scriptsit kept the overlay from ever rendering. iframe-blockednow fires once per blocked iframe, carrying the embed's URL. Re-rendering a placeholder whose copy arrived late no longer emits a second event whosesrchad already been replaced.
- New
d3e426d: Link the custom-site banner to the project's own privacy policy, or show no link at all.
The custom-platform banner always rendered a dead
<a href="#">Privacy Policy</a>: the project's privacy-policy URL never reached the runtime.consentpro-dictionary
getConsentCopyreturnsmessageHtmlWithoutPrivacyLinkalongsidemessageHtml— the same message with the whole privacy sentence removed, cut from the tokenized template so every locale ends on its own sentence terminator.messageHtmlis unchanged.
server / consentpro-types
privacy_page_urlmaps tolegal.privacyPolicyUrl, andsanitizeConfigForClientpublishes that one field.cookiePolicyUrlandcontactEmailstay server-side.
consentpro-v2
- The custom-site banner points the anchor at the configured URL (normalized through
new URL()and restricted to http(s), opened in a new tab), and drops the privacy sentence when no URL is configured.
8eefb34: Withdraw the GPC notice on custom sites the moment a visitor consents.
The notice was resolved once while the elements were built and never revisited, so a visitor who knowingly accepted while a GPC signal was active kept seeing the honored notice for the rest of the page load, in both the banner and the preferences panel. It now tracks the same
GPC.honoredstate the Webflow elements do, which § 7025(c)(6) requires from 1 January 2026.d7331e4: Style the preferences toggle and the dialog backdrop from the web app.
web-app
- The Open preferences colours card now carries Toggle Inactive Track, Toggle Active Track (the former "Preferences Checkbox"), Toggle Circular Handle and Preferences Overlay. Left on their defaults the columns stay
null, so an existing banner looks exactly as before. ColorFieldtakes analphaprop: an opacity track under the hue slider, a checkerboard behind every swatch, and a hex field that accepts#rrggbbaa. Only the overlay opts in — a colour that paints a surface has nothing to be translucent over.- The overlay starts at no backdrop (
#00000000). Choosing a colour on a transparent field lands at 20% opacity rather than staying invisible; the opacity track can still take it back to nothing.
web-components
ConsentPreferencesDialog's backdrop default drops from 10% black totransparent: a dialog washes the page only where a site asks for it. The backdrop box is unchanged otherwise, so click-to-close still works, and any site setting--consentpro-color-overlayis unaffected.
server / consentpro-types
checkbox_inactive_color,checkbox_handle_colorandpreferences_overlay_colorare read, validated and saved onbanner_settingsandbanner_instances, and published ascheckboxInactive,checkboxHandleandpreferencesOverlay. The overlay column validates#rrggbbor#rrggbbaa; the rest stay six digits.
consentpro-v2
- Maps those to
--consentpro-color-toggle-off,--consentpro-color-toggle-thumband--consentpro-color-overlay. The handle keeps whatever colour is picked in both switch states — nothing recolours it from the track any more.
- The Open preferences colours card now carries Toggle Inactive Track, Toggle Active Track (the former "Preferences Checkbox"), Toggle Circular Handle and Preferences Overlay. Left on their defaults the columns stay
Updated dependencies [9a490af]
Updated dependencies [fecdc78]
Updated dependencies [8384efa]
Updated dependencies [d3e426d]
- consentpro-utils@2.1.1
- consentpro-dictionary@0.1.6
2.5.0
Minor Changes
6757be6: Honor a GPC signal that post-dates a visitor's stored consent.
Previously the GPC signal only decided the defaults, so a visitor who consented first and enabled GPC afterwards kept their old consent indefinitely — while the banner still claimed the signal had been honored. The signal is now treated as a live opt-out request that wins over consent saved before it arrived, and a consent given knowingly after the signal was honored wins in turn (CCPA § 7025(c)(3)). The most recent expression of the visitor's preference governs.
consentpro-v2
- The consent cookie gains
gpcHonoredAt: when the runtime first observed the signal that was still active as of that write. It is stamped on every write made while the signal is present and cleared on writes made while it is absent, which is what distinguishes consent given before the signal from consent given knowingly after it. Browsers expose no "enabled at" timestamp, so this is the only recency information available. - On page load, a stored consent that still grants a non-essential category and predates the signal is revoked before blocking, trackers and analytics initialize — so no tracker ever sees the stale consent, and cookies set under the revoked categories are cleaned up on the same load. A consent record is written with
action: 'reject_all'andsource: 'gpc', and is never collapsed into a consent the visitor gives in the second that follows: a processed opt-out request is a compliance event in its own right, not an intermediate state. - The revocation runs only on pages the resolved instance is configured for. On an excluded page nothing can block a tracker or clean up a cookie, so recording an opt-out there would claim an enforcement that never happened; the stored consent is left untouched until the visitor reaches a managed page.
- Consent records now carry the id, action and source captured when the write happened, instead of reading them from the stores when the debounced request fires. Previously any two writes inside a second could hand the first record the second one's attribution.
- The banner is re-shown once on the page load where a category was actually revoked, so the visitor is told their saved preferences changed and gets the chance to consent knowingly instead.
fs-consent-gpcnow reflects whether the signal is being processed, not merely present, and updates live: it flips to"false"the moment a visitor accepts while a signal is active, hiding[fs-consent-element="gpc-honored"]rather than leaving a claim in place that is no longer true. This matters for § 7025(c)(6), which as of 1 January 2026 requires displaying whether the signal was processed.- Scope is unchanged: only Opt-Out and Do Not Sell instances. Opt-In consent is never revoked, and
fs-consent-gpcstays"false"on those instances: the attribute now tracks the same condition the behavior does, so it never claims a signal was processed where nothing acts on one. - A stored consent whose
gpcHonoredAtequals its owntimestampcounts as a knowing override rather than a consent the signal outranks. The two can only match when they were written together, which a consent predating the signal never is — and a custom integration writing the cookie by hand can land both in the same millisecond. FinsweetConsentPro.consents.set()now stampsaction: 'submit'andsource: 'api', like every other write path. Previously it inherited whatever wrote the consent before it, which with GPC in play would have recorded a site's own API call as a GPC-driven opt-out. It also means API-driven consent is recorded at all, where before the missing action caused the record to be dropped.
Existing GPC visitors who had granted consent are opted out once, on their next page view, and see the banner that one time.
documentation
developer/custom-integration.mddocumentsgpcHonoredAtand shows how to carry it forward when writing the cookie by hand. A custom integration that omits the stamp while a signal is active writes a consent that Consent Pro cannot distinguish from one given before the signal, and revokes on the visitor's next Webflow page view.
consentpro-types / server
ConsentSourceandConsentRecordSchema.sourceaccept'gpc'. The consents endpoint validates against that enum, so without this the GPC records would be rejected and silently dropped.
- The consent cookie gains
Patch Changes
- consentpro-dictionary@0.1.5
- consentpro-utils@2.1.0
2.4.2
Patch Changes
- Updated dependencies [f528b4f]
- consentpro-dictionary@0.1.4
- consentpro-utils@2.1.0
2.4.1
Patch Changes
- 2836963: fix: send only a Google Consent Mode
updatefrom the runtime and leave the baselinedefaultto the Consent Pro GTM template, which declares it at container initialization
2.4.0
Minor Changes
55656ae: Add optional public hostname and path prefix so reverse-proxied sites publish a hostname.json for the visitor host and expose the site information the runtime uses to compose public URLs.
The Webflow domain is unchanged. Custom-domain sites publish
publicHostnameand, when mounted under a prefix,pathPrefix. The runtime composes these with locale subdirectories and page slugs. Sites without these settings are unaffected.
Patch Changes
- Updated dependencies [55656ae]
- consentpro-utils@2.1.0
- consentpro-dictionary@0.1.3
2.3.6
Patch Changes
- f4354d3: fix: do not block Google Tag Manager container scripts (
gtm.js?id=GTM-*)
2.3.5
Patch Changes
03bd744: chore(deps): resolve open Dependabot PRs and clear the
pnpm auditbacklogDirect dependency updates:
app: axios 1.16.0 → 1.18.0, posthog-js 1.336.1 → 1.417.0consentpro-v1: nanoid 5.1.6 → 5.1.16, esbuild 0.27.3 → 0.28.1consentpro-v2: esbuild 0.27.3 → 0.28.1server: hono 4.12.21 → 4.12.34consentpro-debugger: vite 7.3.2 → 7.3.6
Transitive advisories are pinned to patched versions via
pnpm.overrides(tar, undici, brace-expansion, js-yaml, protobufjs, fast-uri, ip-address, postcss, nanoid, form-data, ws, adm-zip, shell-quote, sharp, dompurify, esbuild), takingpnpm auditfrom 89 findings (1 critical, 41 high, 35 moderate, 12 low) to 0.The posthog-js bump also removes the
@opentelemetry/*andprotobufjssubtrees, which were never reachable from the shipped extension bundle. Telemetry configuration is unchanged and the pinneddefaults: '2025-05-24'still applies, so capture behaviour is identical.Two advisories have no published fix and are recorded in
pnpm.auditConfig.ignoreCvesrather than resolved — CVE-2026-56876 (extract-zip, via@cloudflare/puppeteer) and CVE-2025-14505 (elliptic, via@webflow/webflow-cli). Neither is reachable from a shipped artifact.
2.3.4
Patch Changes
- Updated dependencies [4d7fcb3]
- consentpro-dictionary@0.1.2
- consentpro-utils@2.0.3
2.3.3
Patch Changes
3ea0afe: Ensure a project record per site, and dual-write core config to D1.
server
- New
services/v2/projects:ensureProject,getProjectUuidBySiteId,linkSiteAuthorizationProject. Project creation is idempotent and race-safe, and revives a soft-deleted project on re-authorization. - The Webflow OAuth callback creates the project and stamps it onto the site authorization on first authorization. Failures are logged and never block authorization.
- Temporary R2 → D1 dual-write (
configurator/temporary-d1-mirror):saveCoreConfigand site migration mirror the core runtime config intoproject_configs,webflow_project_configs,banner_instances,config_providersandconfig_trackers. R2 stays the source of truth and the mirror soft-fails, so a D1 error can never fail a publish. Sites only begin mirroring once they have a project row, so existing sites stay unmirrored until they re-authorize. project_uuidis stamped on scans, scan schedules, action item resolutions, policy profiles and policy documents, without overwriting a value that is already set.- Schema:
project_uuidforeign keys move fromON DELETE set nulltoON DELETE cascade, andproject_configsgainslocales. - Migrations
0038(addsproject_configs.locales) and0039(the foreign key change).0039is hand-written rather than drizzle-generated: the generated table rebuild issuesDROP TABLE, which on D1 fires the pre-existingpolicy_documents→policy_profilescascade and empties the table, because D1 runs each migration inside a transaction wherePRAGMA foreign_keys=OFFis silently ignored. It usesALTER TABLE DROP COLUMN/ADD COLUMNinstead, so no table is ever dropped. Side effect:project_uuidbecomes the last column on the six affected tables.
consentpro-v2
- The consent cookie is now always written with
sameSite: 'lax', andsecureis derived fromlocation.protocolinstead of configuration.storage.sameSiteandstorage.secureare no longer read by the v2 runtime.
consentpro-types
ConsentV2TrackerDefinitiongains an optionalnameforcookietrackers and an optionalkeyforlocal-storage/session-storagetrackers.
- New
34c5fc6: fix: invalidate stored consent cookie when the user changes region
- consentpro-dictionary@0.1.1
- consentpro-utils@2.0.3
2.3.2
Patch Changes
- Updated dependencies [a0173e0]
- Updated dependencies [81c0689]
- Updated dependencies [3fb6710]
- Updated dependencies [9d4c732]
- consentpro-dictionary@0.1.0
- consentpro-utils@2.0.3
2.3.1
Patch Changes
cb1d15a: chore: cleanup runtime bundle to remove non-prod strings and ensure proper treeshake
2487ddd: feat: runtime unblocking for manually blocked images
- Added
blocking/images.tsto the v2 runtime: images holding their URL infs-consent-srcwithfs-consent-categoriesare loaded automatically once the visitor accepts the required categories, and unloaded again when consent is revoked. Unblock-only by design — a live imagesrcfires its request immediately and cannot be intercepted. - Emit
image-blocked/image-unblockedevents, mirroring iframes. - Simplified the Consent Manager image instructions and docs: the manual consent-callback snippet is no longer needed, only the attribute swap.
- Added
Updated dependencies [cb1d15a]
- consentpro-utils@2.0.2
- consentpro-dictionary@0.0.11
2.3.0
Minor Changes
- f4ab509: feat: debugger auth
- 4435f7e: feat: same-origin library imports
Patch Changes
- 46a579b: chore: do not include license in CDN response
- 20a5755: fix: only load debugger in staging webflow.io
- 1f19ea2: fix: allow no animations
- 5407450: fix cookie declaration hydration
- fa75381: Enhance policy generator
- Updated dependencies [494b74c]
- Updated dependencies [73a3472]
- Updated dependencies [5407450]
- consentpro-dictionary@0.0.10
- consentpro-utils@2.0.1
2.2.9
Patch Changes
- Updated dependencies [e5d2aad]
- consentpro-dictionary@0.0.9
- consentpro-utils@2.0.1
2.2.8
Patch Changes
- Updated dependencies [353af98]
- consentpro-dictionary@0.0.8
- consentpro-utils@2.0.1
2.2.7
Patch Changes
- 023a021: fix(v2): apply overflow lock on iOS when disabling scroll
- 4edff91: fix: update show function to display banner when preferences element is absent; add test for re-showing banner without preferences element
2.2.6
Patch Changes
- ded7f80: update namings in usages of
consent-pro-**toconsentpro-** - 73aa49f: feat: consent pro debugger
- Updated dependencies [ded7f80]
- Updated dependencies [73aa49f]
- consentpro-dictionary@0.0.7
- consentpro-utils@2.0.1
2.2.5
Patch Changes
- Updated dependencies [ef22755]
- Updated dependencies [9cf51bd]
- consentpro-utils@2.0.1
- consentpro-dictionary@0.0.6
2.2.4
Patch Changes
- 9309ade: fix: preserve scroll on scrollable children
2.2.3
Patch Changes
- 9f95405: fix: make GPC flexible to anyone with GPC enabled, not just specific regions
- Updated dependencies [1a79dde]
- consentpro-dictionary@0.0.5
- consentpro-utils@2.0.0
2.2.2
Patch Changes
- ba3df41: fix: respect original script
typeafter unblocking - Updated dependencies [1a79dde]
- consentpro-dictionary@0.0.4
- consentpro-utils@2.0.0
2.2.1
Patch Changes
- 02e434b: fix: prevent race conditions when interacting with third party libraries
- c1bc061: fix: correct event listener target from window to document in waitDOMReady function
2.2.0
Minor Changes
- 6509630: feat: disable scroll behavior controls
Patch Changes
- consentpro-dictionary@0.0.3
- consentpro-utils@2.0.0
2.1.2
Patch Changes
- 683eeb0: chore: Update dependencies to latest versions across all packages and fix pnpm audit issues
2.1.1
Patch Changes
- ef4a3e5: fix(core-v2): remove double URL-encoding of fs-consent cookie
- js-cookie already calls handles encoding and decoding
- add test for it and for backward compatible encoded versions
2.1.0
Minor Changes
- 7cd69e6: feat: support clicks in blocked iframes
- 171ba65: feat: support blocking iframes inside Webflow lightboxes
- 0f15342: feat: support GPC signal
Patch Changes
- 3beee94: fix: unblock essential scripts marked with type="fs-consent"
- 171ba65: fix: don't process application/json scripts
- 7caea86: fix: flash of unstyled content when elements exist visible on the current page
- 7e10aef: fix: ensure that elements outside the shadow root are hidden
- 171ba65: fix: remove false positives in
iframe-blockedAPI events - f083ccd: improvement: no longer store provider & tracker metadata in each consent log
- 171ba65: fix: recursively process deeply nested nodes with the mutation observer
- 2a279e8: fix: only load consent pro when there is a matching region
- 9ad2133: fix: allow clicks outside preferences when focus-trap is activated
- Updated dependencies [e9bd587]
- Updated dependencies [6029a6c]
- consentpro-utils@2.0.0
- consentpro-dictionary@0.0.2