<!-- Capcat 文档「前端组件」的 Markdown 版本（对应页面 https://capcat.ai/cn/guide/widget.html）。全部文档索引：https://capcat.ai/llms.txt -->

# 前端组件

验证组件是一个标准 Web Component（`<cap-widget>`），一行 `<script>` 引入，任何框架都能用。

## 引入

```html
<script src="https://capcat.ai/widget/cap.js"></script>
```

> **提示**
>
> 该文件由 Capcat 官方托管（capcat.ai），不依赖第三方 CDN，国内外访问都稳定。

## 表单方式（零 JavaScript）

组件放在 `<form>` 内时，会自动注入隐藏的 `cap-token` 字段并随表单一起提交：

```html
<form action="/submit" method="POST">
  <cap-widget data-cap-api-endpoint="https://api.capcat.ai/<site-key>/"></cap-widget>
  <button type="submit">提交</button>
</form>
```

## JavaScript 方式（SPA / 自定义流程）

监听 `solve` 事件拿到 token，自行决定何时发送：

```js
const widget = document.querySelector("cap-widget");
widget.addEventListener("solve", (e) => {
  const token = e.detail.token;
  // 把 token 发给你的服务端、解锁提交按钮等
});
```

## 常用属性

| 属性 | 说明 |
| --- | --- |
| `data-cap-api-endpoint` | 验证服务地址，格式 `https://api.capcat.ai/<site-key>/`（site key 在[控制台](https://capcat.ai/cn/guide/console.md)创建站点时获得），必填 |
| `data-cap-theme` | 设为 `auto`，组件自动贴合页面已有样式——见[自动主题](#自动主题) |
| `data-cap-branding` | 设为 `hidden` 去掉组件角落的 Capcat 角标——见[隐藏角标（付费套餐）](#隐藏角标-付费套餐) |
| `data-cap-worker-count` | 求解使用的 Web Worker 数量，默认按设备核数 |

## 样式定制

组件渲染在 Shadow DOM 内，页面 CSS 不会意外破坏它。定制走两条受支持的通道——自动主题与 `--cap-*` CSS 变量，二者可自由组合。

### 自动主题

加上 `data-cap-theme="auto"`，组件自动从页面采样外观，一行 CSS 都不用写：

```html
<cap-widget data-cap-theme="auto" data-cap-api-endpoint="..."></cap-widget>
```

采样内容：

- **字体、字号与正文色**——按组件所在位置的继承链取值，字号跟随参照输入框
- **背景**——取第一个不透明的祖先背景，贴合所在卡片/面板，深色页面自动适配
- **边框色与圆角**——向同一 `<form>` 内的第一个文本输入框对齐；不在表单里时就近取周围的表单控件，再退就近按钮（只取圆角，封顶 `24px`）；复选框圆角按比例缩放
- **强调色**——表单设置了 `accent-color` 则优先使用，否则回落正文色（用于加载环、焦点框与疑难链接）

自动主题只补齐你没有显式设置的变量：CSS 里声明过的 `--cap-*` 变量始终优先，因此 `auto` 可与下方手动覆盖组合使用。系统深浅色切换时会自动重新采样；如果你的应用在运行时切主题（如切换 class），切换后重新设置一次属性（`el.setAttribute("data-cap-theme", "auto")`）即可重新采样。

### CSS 变量

在 `cap-widget` 元素上设置以下任意变量（示例值即默认值）：

```css
cap-widget {
  --cap-background: #fdfdfd;
  --cap-border-color: #dddddd8f;
  --cap-border-radius: 14px;
  --cap-widget-width: 260px;
  --cap-widget-height: 58px;
  --cap-widget-padding: 14px;
  --cap-gap: 15px;
  --cap-color: #212121;
  --cap-font: system-ui, sans-serif;
  --cap-font-size: 15px;
  --cap-checkbox-size: 25px;
  --cap-checkbox-border: 1px solid #aaaaaad1;
  --cap-checkbox-border-radius: 6px;
  --cap-checkbox-background: #fafafa91;
  --cap-spinner-color: #000;
  --cap-spinner-background-color: #eee;
  --cap-focus-ring: #0066cc;
}
```

常用配方——让组件和其他表单字段一样占满容器宽度：

```css
cap-widget {
  display: block;
  --cap-widget-width: 100%;
}
```

### 隐藏角标（付费套餐）

`data-cap-branding="hidden"` 可去掉组件角落的 Capcat 角标。这是付费套餐权益：先在[控制台](https://capcat.ai/cn/guide/console.md)为站点开启**隐藏组件 Capcat 角标**——免费站点上服务端会让角标重新出现。

## Content Security Policy

如果页面下发了 `Content-Security-Policy`，组件需要以下放行。所有资源都由 `capcat.ai` / `api.capcat.ai` 提供，不会访问任何第三方 CDN。

| 指令 | 值 | 原因 |
| --- | --- | --- |
| `script-src` | `https://capcat.ai` `'unsafe-eval'` `'wasm-unsafe-eval'`，外加 `'unsafe-inline'` **或** nonce | `cap.js` 本体、pako 回退脚本、编译求解用的 WebAssembly 模块，以及在 sandboxed `srcdoc` iframe 里运行的 instrumentation 检查。该 iframe 继承页面 CSP：其脚本是内联的（所以要 `'unsafe-inline'` 或 nonce），环境检查用到 `new Function` / `eval`（所以要 `'unsafe-eval'`）。缺了这些检查会静默失败，组件等满 20 秒超时后报错 |
| `connect-src` | `https://api.capcat.ai https://capcat.ai` | challenge / redeem 接口调用，以及拉取 `cap_wasm_bg.*.wasm` |
| `worker-src` | `blob:` | 求解 worker 由 Blob URL 创建（不支持 `worker-src` 的浏览器用 `child-src`） |
| `style-src` | `'unsafe-inline'` **或** nonce | 组件会向 shadow root 注入一个 `<style>` |

一份其他部分仍然收紧的最小策略：

```
Content-Security-Policy: default-src 'self';
  script-src 'self' https://capcat.ai 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval';
  connect-src 'self' https://api.capcat.ai https://capcat.ai;
  worker-src blob:;
  style-src 'self' 'unsafe-inline'
```

### 用 nonce 代替 `'unsafe-inline'`

在 `cap.js` 加载**之前**设置以下全局变量，组件会把你的 nonce 打到它注入的所有内容上：

- `window.CAP_CSS_NONCE`——shadow root 里的 `<style>`（这样 `style-src` 就不需要 `'unsafe-inline'`）。
- `window.CAP_SCRIPT_NONCE`——instrumentation iframe 里的内联脚本，以及组件追加的第三方脚本标签（Account Defense 配置下的 Turnstile / reCAPTCHA）。未设置时回退到 `CAP_CSS_NONCE`。无论是否用 nonce，`script-src` 都要保留 `'unsafe-eval'`；nonce 只能替代 `'unsafe-inline'`。

```html
<script nonce="r4nd0m">
  window.CAP_CSS_NONCE = "r4nd0m";
  window.CAP_SCRIPT_NONCE = "r4nd0m";
</script>
<script src="https://capcat.ai/widget/cap.js" nonce="r4nd0m" async defer></script>
```

### 自行托管资源

如果某条指令无法放行 `capcat.ai`，可以把组件指向你自己源站上的副本，并相应调整上面的指令：

- `window.CAP_CUSTOM_WASM_URL`——`cap_wasm_bg.wasm` 的绝对地址（可从 `https://capcat.ai/widget/cap_wasm_bg.wasm` 下载）。
- `window.CAP_PAKO_URL`——`pako_inflate.min.js` 的地址。该回退只在没有 `DecompressionStream` 的浏览器上加载；默认副本带 Subresource Integrity 校验，自定义地址则原样加载。

这些全局变量请在脚本里用字符串赋值。其他类型的值 widget 一律忽略——包括页面里恰好同名的元素（例如用户内容中的 `<a id="CAP_CSS_NONCE">`）；资源地址只接受 `https:` 或与页面同源的地址。

React / Vue / Svelte 等框架接入示例，参见上游文档 [Widget 章节](https://trycap.dev/guide/widget.html)。
