跳转到主要内容
Cookie 同意是每个公开网站都要做的工作,但几乎所有 indie 都拖到买家法务团队 flag 才补。vibestrap ship 了一个能直接跑的 banner,所以你不用在”快上线”和 “day-one 合规”之间二选一:
  • 🍪 默认 opt-in —— 分析脚本(Google Analytics、Microsoft Clarity、 PostHog)等用户点 Accept 后才加载。同意前不收集任何数据。
  • ⚖️ 全球统一合规框架 —— 一套”您的隐私选择”机制覆盖 GDPR(欧盟/英国)、 CCPA / CPRA(加州)、LGPD(巴西)、PIPL(中国);banner 或页脚一键退出。
  • 🧹 撤回时清理 —— 用户先 Accept 后 Decline 了 analytics,我们会把已经 设的 cookie 清掉。GDPR 第 7(3) 条。
  • 🌍 双语 —— en / zh 跟 vibestrap 现有 i18n 一致,不需要额外语言检测。
  • 🎨 两按钮 banner —— 只有 Accept / Reject,没有第三个”自定义”按钮。 分类粒度控制放在页脚的 “Cookie 偏好” 链接里(Hick’s 定律:选项越少决策越快)。
  • 🛠️ 基于 vanilla-cookieconsent (9k+ stars, MIT)。通过 CSS 变量轻松改皮肤。
架构有意分成三层:
文件做什么
vanilla-cookieconsentDOM 注入、GDPR/CCPA 逻辑、持久化
包装src/components/cookie-consent/cookie-consent.tsxReact Provider、语言挂钩、生命周期
消费useConsent() in src/analytics/analytics.tsx门控实际跟踪脚本

前置条件

  • 无需安装 —— vanilla-cookieconsent 已经是依赖。
  • siteConfig.cookieConsent.enable 默认 true(banner 默认开启)。

一步步配置

1. 在 src/config/site.ts 确认开启

cookieConsent: {
  enable: true,        // 挂载 banner
  revision: 1,         // cookie 变了 bump 这个数,强制重新同意
  expiresAfterDays: 365,
},

2. 验证 analytics 门控

src/analytics/analytics.tsx 里脚本被分成两组:
{/* 不需 consent —— cookie-free provider */}
{siteConfig.analytics.vercel && <VercelAnalytics />}
{siteConfig.analytics.plausible && <PlausibleScript />}
{siteConfig.analytics.umami && <UmamiScript />}

{/* 需 consent —— 会下 cookie */}
{siteConfig.analytics.googleAnalytics && allowAnalytics && <GoogleAnalytics />}
{siteConfig.analytics.posthog && allowAnalytics && <PostHogScript />}
{siteConfig.analytics.clarity && allowAnalytics && <ClarityScript />}
allowAnalytics 只有用户点了 Accept(或者之前 12 个月内已同意)才是 true 如果你加了新 analytics provider 或换了一个,把 siteConfig.cookieConsent.revision 加 1。所有之前同意过的用户会再看到 banner,可以在新政策下重新同意。这是 GDPR 最佳实践。

4. 在浏览器里验证

pnpm dev
dev 模式下 banner 故意跳过,免得开发时每次刷新都被 banner 砸脸(看 cookie-consent.tsx 里的 process.env.NODE_ENV 检查)。要看真 banner:
pnpm build && pnpm start
# 或者访问生产环境
你应该看到:
  • 首次访问右下角弹出 toast
  • 两个按钮:Accept all / Reject all
  • 底部链接:Privacy Policy · Cookie Policy · Your Privacy Choices

用户旅程 —— 每个场景都对齐业界标杆

下面这张矩阵把每个常见用户流映射到 vibestrap 的行为以及 Stripe / Microsoft / Vercel / Cloudflare / Linear / OpenAI 同等场景的行为。实现处处对齐业界—— 没有任何独家或反直觉的设定。
场景vibestrap 行为业界标准
首次访问右下角弹 banner同(Stripe / Vercel / Linear / OpenAI)
Accept 后 13 个月内访问不弹 banner,GA + Clarity 加载
Reject 后 13 个月内访问不弹 banner,GA + Clarity 不加载
同意过期后访问(> 13 个月)banner 重新弹出(等同首次)同 —— Stripe/MS 也用 13mo
刷新 / 导航不做选择banner 持续显示直到选择
点 X 关闭 banner 不选择视为”全部拒绝”—— 仅 necessary同 —— GDPR 禁止当 Accept 处理
政策版本变更 (revision bump)banner 重弹(强制重新同意)同 —— Stripe/MS 也用 version 字段
清浏览器数据banner 重弹(cookie 没了)
隐身 / 无痕窗口每次新会话都弹 banner
跨设备每个设备独立决定同 —— consent 是 per-browser-storage
浏览器发 Sec-GPC: 1(Brave / DuckDuckGo / Firefox+ext)不弹 banner,自动 Reject(仅 necessary)同 —— Microsoft + 2024+ 美国州法要求
banner 上点 Privacy Policy跳到 /privacy,banner 保持可见同 —— 阅读期间不能丢失 consent 状态
footer 点 Cookie Preferences重新打开 preferences modal同 —— GDPR 第 7(3) 条要求
接受后再关闭分析 toggle下次页面加载时删除 _ga / _clck / MUID同 —— GDPR 第 7(3) 条要求
拒绝后再开启分析 toggle下次渲染时加载 GA + Clarity
Reject 对站点功能影响所有功能正常工作(登录、支付、订阅)同 —— GDPR 禁止”cookie 墙”
Reject 时遇到 ?ref=xxxcookie 不写入(marketing 类已关)同 —— Stripe / Vercel 同款门控
默认有效期 395 天(约 13 个月),跟下列对齐:
  • Stripe (13 个月)
  • Microsoft (13 个月)
  • Cloudflare (13 个月)
这是英国 ICO 和法国 CNIL 推荐的上限 —— 足够长让回访用户不被反复打扰, 足够短保证同意”新鲜”。选更短窗口(Vercel / Linear / Notion 用 6 个月)也合规; 我们选 13 个月是为了对齐最严肃的参考实现。要调整,改 siteConfig.cookieConsent.expiresAfterDays

自定义 banner 文案

所有 banner 文字在 messages/{en,zh}.jsonCookieConsent namespace。 改标题、按钮、描述就改这些 key —— 两个文件同步改(i18n audit 会抓 drift)。

自定义 banner UI

vanilla-cookieconsent 暴露了大约 30 个 CSS 变量(颜色、圆角、阴影、字体)。 override 这些变量,或加自定义 CSS 层:
:root {
  --cc-bg: #fff;
  --cc-primary-color: var(--accent-tech);  /* 匹配 vibestrap 品牌色 */
  --cc-border-radius: 0.5rem;
  /* 完整列表: https://cookieconsent.orestbida.com/advanced/customization.html */
}
要完全替换 UI 也行 —— 但默认已经是 Linear / Stripe 风格的 toast,基本够用。

撤回入口

GDPR 第 7(3) 条要求撤回同意必须跟给同意一样容易。vibestrap 在页脚 Legal section 渲染了一个 Cookie Preferences 链接,点击重新打开 modal。 由 src/components/cookie-consent/cookie-preferences-link.tsx 挂载,自动接 好 —— 不要删掉。

常见坑

  1. dev 看不到 banner。这是有意的 —— dev 模式短路到”全部同意”,所以你 测 analytics 不会被 banner 烦。用 pnpm build && pnpm start 看真 banner。
  2. 加了新 tracker 忘了 bump revision。已有用户不会被重新询问,会在旧 同意下被新 tracker 跟踪(旧同意没覆盖新 tracker)。加新 tracker 的同一个 PR 必须 bump siteConfig.cookieConsent.revision
  3. Decline 后 analytics 还在加载。检查 src/analytics/analytics.tsx 里 的脚本渲染是不是真的在 allowAnalytics 后面 —— 加新 tracker 时容易忘。 约定:任何会下 cookie 的都门控,cookie-free 的(Vercel / Plausible / Umami)无条件渲染。
  4. banner 渲染出破碎 HTML。如果你自定义了 banner footer 字符串(隐私 政策、隐私选择链接),确保 HTML 合法 —— 库会 raw 渲染。
  5. 语言不匹配。banner 读 <html lang="…">(由 next-intl 设置)。如果 你的自定义 layout 设了别的 lang,banner 会 fallback 到英文。

“您的隐私选择” —— 全球统一框架

banner footer 链接到 /privacy#privacy-choices,这是隐私政策里全球统一 的退出权利章节,涵盖所有访问者的退出权:GDPR(欧盟/英国)、CCPA / CPRA (加州)、LGPD(巴西)、PIPL(中国)。退出机制对每个访问者都一样 (banner、页脚链接或邮件);章节里再点名每个地区的具名权利 (比如加州的”Do Not Sell My Personal Information”),让搜这个法定术语 的监管也能找到。 这跟 Apple、Microsoft、Stripe 的做法一致 —— 它们都从加州专属的 “Do Not Sell” 抬头转向了全球统一的”Your Privacy Choices”框架, 下挂各地具名子权利。如果你重点做加州市场,想给 opt-out 加专门的表单页, 可以建一个 /privacy/privacy-choices 页;内联章节是 indie 阶段的默认。 如果你完全 cookie-free(只用 Vercel Analytics / Plausible / Umami),不想 要 banner,设:
// src/config/site.ts
cookieConsent: { enable: false, revision: 1, expiresAfterDays: 365 },
这仍然挂载 React Context(useConsent() 还能用),但把每个用户当成”已同意 全部”处理。banner UI 完全跳过。

官方资源