> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibestrap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Cookie 同意

> 由 vanilla-cookieconsent 驱动的 GDPR / CCPA / 全球合规 banner。默认 opt-in、自动门控分析脚本、开箱 en + zh 双语。

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`](https://github.com/orestbida/cookieconsent)**
  (9k+ stars, MIT)。通过 CSS 变量轻松改皮肤。

架构有意分成三层:

| 层  | 文件                                                 | 做什么                      |
| -- | -------------------------------------------------- | ------------------------ |
| 库  | `vanilla-cookieconsent`                            | DOM 注入、GDPR/CCPA 逻辑、持久化  |
| 包装 | `src/components/cookie-consent/cookie-consent.tsx` | React Provider、语言挂钩、生命周期 |
| 消费 | `useConsent()` in `src/analytics/analytics.tsx`    | 门控实际跟踪脚本                 |

## 前置条件

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

## 一步步配置

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

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

### 2. 验证 analytics 门控

`src/analytics/analytics.tsx` 里脚本被分成两组:

```ts theme={null}
{/* 不需 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`。

### 3. cookie 变了就 bump revision

如果你加了新 analytics provider 或换了一个,把 `siteConfig.cookieConsent.revision`
加 1。所有之前同意过的用户会再看到 banner,可以在新政策下重新同意。这是
GDPR 最佳实践。

### 4. 在浏览器里验证

```bash theme={null}
pnpm dev
```

dev 模式下 banner 故意跳过,免得开发时每次刷新都被 banner 砸脸(看
`cookie-consent.tsx` 里的 `process.env.NODE_ENV` 检查)。要看真 banner:

```bash theme={null}
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=xxx`**                             | cookie 不写入(marketing 类已关)          | 同 —— Stripe / Vercel 同款门控            |

## Cookie 有效期(13 个月)

默认有效期 **395 天(约 13 个月)**,跟下列对齐:

* **Stripe** (13 个月)
* **Microsoft** (13 个月)
* **Cloudflare** (13 个月)

这是英国 ICO 和法国 CNIL 推荐的上限 —— 足够长让回访用户不被反复打扰,
足够短保证同意"新鲜"。选更短窗口(Vercel / Linear / Notion 用 6 个月)也合规;
我们选 13 个月是为了对齐最严肃的参考实现。要调整,改 `siteConfig.cookieConsent.expiresAfterDays`。

## 自定义 banner 文案

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

## 自定义 banner UI

`vanilla-cookieconsent` 暴露了大约 30 个 CSS 变量(颜色、圆角、阴影、字体)。
override 这些变量,或加自定义 CSS 层:

```css theme={null}
: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 consent

如果你完全 cookie-free(只用 Vercel Analytics / Plausible / Umami),不想
要 banner,设:

```ts theme={null}
// src/config/site.ts
cookieConsent: { enable: false, revision: 1, expiresAfterDays: 365 },
```

这仍然挂载 React Context(`useConsent()` 还能用),但把每个用户当成"已同意
全部"处理。banner UI 完全跳过。

## 官方资源

* vanilla-cookieconsent 文档:
  [cookieconsent.orestbida.com](https://cookieconsent.orestbida.com/)
* GDPR 第 7 条(同意):
  [gdpr-info.eu/art-7-gdpr](https://gdpr-info.eu/art-7-gdpr/)
* CCPA / CPRA:
  [oag.ca.gov/privacy/ccpa](https://oag.ca.gov/privacy/ccpa)
