# SumUp Circuit 设计系统

## SumUp Circuit 设计系统界面预览

尝试输入格式错误的金额、更改商品名称、切换备注字段，并查看本地快照。

## SumUp Circuit 设计系统设计参考

原生使用 @sumup/circuit-ui 8.15.0 和 @sumup/design-tokens 7.3.1 浅色 CSS；原创本地商品编辑器。

### 商户端浅色界面表面与文本

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --cui-bg-normal | #fff | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-bg-subtle | #f0f1f5 | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-bg-strong | #000 | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-bg-accent-strong | #0f131a | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-fg-normal | #0f131a | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-fg-subtle | #6a737c | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-fg-on-strong | #fff | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-fg-success | #018850 | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-fg-danger | #de331d | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
| --cui-border-normal | #aeb6be | 商户端浅色语义参考；将界面表面颜色用作前景色前，请确认它适用的配色组合。 |
### 间距单位

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --cui-spacings-bit | 4px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-byte | 8px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-kilo | 12px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-mega | 16px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-giga | 24px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-tera | 32px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-peta | 40px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-exa | 48px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
| --cui-spacings-zetta | 56px | 从锁定版本的 CSS 导出中复制的命名间距级别。 |
### 圆角半径

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --cui-border-radius-bit | 4px | 根据控件或界面表面的用途选择形状。 |
| --cui-border-radius-byte | 8px | 根据控件或界面表面的用途选择形状。 |
| --cui-border-radius-kilo | 12px | 根据控件或界面表面的用途选择形状。 |
| --cui-border-radius-mega | 16px | 根据控件或界面表面的用途选择形状。 |
| --cui-border-radius-circle | 100% | 根据控件或界面表面的用途选择形状。 |
| --cui-border-radius-pill | 999999px | 根据控件或界面表面的用途选择形状。 |
### 字体系列与字重

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --cui-font-stack-default | aktiv-grotesk, Helvetica, Arial, system-ui, sans-serif, "Segoe UI", Roboto, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" | 此示例使用可用的回退字体，不会加载 Aktiv Grotesk。 |
| --cui-font-stack-mono | Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace | 此示例使用可用的回退字体，不会加载 Aktiv Grotesk。 |
| --cui-font-weight-regular | 400 | 此示例使用可用的回退字体，不会加载 Aktiv Grotesk。 |
| --cui-font-weight-bold | 700 | 此示例使用可用的回退字体，不会加载 Aktiv Grotesk。 |
### 主要操作状态

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| --cui-bg-accent-strong | #0f131a | 分别保留常规、悬停、按下和禁用状态。 |
| --cui-bg-accent-strong-hovered | #3b3f46 | 分别保留常规、悬停、按下和禁用状态。 |
| --cui-bg-accent-strong-pressed | #6a737c | 分别保留常规、悬停、按下和禁用状态。 |
| --cui-bg-accent-strong-disabled | rgba(15, 19, 26, .4) | 分别保留常规、悬停、按下和禁用状态。 |
### 输入框与按钮的公开属性

| 名称 / 规则 | 值 / 配置 | 用途 |
| --- | --- | --- |
| Input.label | required string | 明确字段用途。 |
| Input.invalid | boolean | 原生无效样式与无障碍状态。 |
| Input.validationHint | string | 在控件下方显示具体说明。 |
| Button.variant | primary / secondary / tertiary | 此锁定版本中的操作层级。 |
| Button.size | s / m | 版本 8.15.0 中当前使用的尺寸值。 |
| Button.stretch | boolean | 让本地审核操作填满父容器宽度。 |
## SumUp Circuit 设计系统完整指南

实用指南，介绍如何为真实界面选择、应用和评审此系统。

完整指导、实现起点和源代码链接。

## SumUp Circuit 设计系统商户编辑器编排

### 将商户输入与顾客端展示分开

> 商品编辑器和类似收据的预览展示了同一状态的两个视角。简短的表单用于填写商品名称、设置示例金额，并选择是否显示顾客备注字段。预览会展示这些设置的效果，但不会创建结账流程或索取付款信息。

### 使用原创产品插画

几何风格的印刷图案由 CSS 创作，并附有无障碍描述。它让顾客端预览中出现一个易于辨认的物件，而不是空白图片框，同时避免使用虚构的商户照片或复制的 SumUp 产品素材。

## SumUp Circuit 设计系统语义主题与软件包范围

### 使用精确匹配的锁定主题

此示例使用 @sumup/circuit-ui 8.15.0 和 @sumup/design-tokens 7.3.1。参考表数据读取自该软件包的 light.css。上游文档目前已转向 @sumup-oss 命名空间；这里展示的是可复现的锁定版本集成，并非声称较旧的命名空间是最新版本。

### 加载组件样式与设计令牌样式

React 入口会同时导入组件样式表和浅色设计令牌文件。如果尚未定义组件类名所需的 CSS 自定义属性，仅有组件类名是不够的。作用域化的深色调色板也需要完整的基础主题，以提供通用的非颜色值。

## SumUp Circuit 设计系统中性界面表面与操作对比度

### 让商品成为视觉焦点

编辑器使用白色界面表面和深色前景，顾客端预览则置于低调的中性背景之上。原创插画在商品图片内加入恰到好处的色彩，同时让原生主要操作清晰易读、易于找到。

### 让预览操作明显不可交互

顾客侧页脚是标有“仅供预览”的文本块，而不是会让人误以为结账流程即将可用的禁用“支付”按钮。唯一真正的提交操作属于编辑器，用于核对本地商品。这样有助于用户了解示例实际支持的交互范围。

## SumUp Circuit 设计系统中的字体与回退字体

### 如实使用主题字体栈

已发布的字体栈以 Aktiv Grotesk 开头，并包含 Helvetica、Arial 和系统回退字体。此离线示例不会下载首选字体。原生 Headline 和 Body 组件仍能呈现预期的层次结构，而最终字形则取决于系统中可用的字体。

### 让金额与币种保持在一起

Intl.NumberFormat 会根据经过验证的本地金额生成欧元格式。价格的视觉强调高于商品描述，并保留在顾客预览中。草稿格式错误时会显示长破折号，而不是容易误导人的零或只解析成功的部分金额。

## SumUp Circuit 设计系统中的间距与圆角界面表面

### 使用具名的步长序列

令牌刻度采用 bit、byte、kilo、mega 等名称。这些是设计标签，并非二的幂次运算。表格保留了精确的像素值，避免开发者根据命名方式臆造刻度。

### 对齐控件边缘，同时保留层次

表单采用统一的垂直间距和全宽主操作按钮。预览使用较大的圆角外层背景，以及较小的收据内层区域。在手机上，预览显示在表单之后；先缩小内边距，再压缩控件。

## SumUp Circuit 设计系统中的商品验证与草稿状态

### 格式化前验证完整字符串

应用接受一至五位整数，以及最多两位小数，并要求数值为正数。这是示例自行设定的规则，可避免 parseFloat 接受 24abc 这样的不完整字符串，并会在提交后将焦点移到第一个无效的原生 Input。

### 任何编辑都会清除过期的核对结果

编辑名称、金额或备注可见性，都会使之前的快照失效。成功状态包含已核对的商品名称和金额，因此对应的是一份具体草稿。代码不会将此结果存入后端，也不会创建支付请求。

## SumUp Circuit 设计系统中的输入恢复与预览语义

### 检查两个字段的错误处理

商品名称和价格使用原生必填标签、无效状态和 validationHint。源代码保留了输入框引用，以便将焦点移回第一个验证失败的字段。无需输入真实的顾客或银行卡数据，即可测试错误恢复。

1. 清空名称并提交，查看第一个字段的错误提示。

2. 尝试输入 24abc 或 0，然后将金额更正为 24.50。

3. 在本地核对后编辑字段，过期的快照便会消失。

### 如实标注插图和静态预览

插图带有简短的图片用途说明。顾客备注区域是说明性占位内容，位于明确标注的预览中，并非伪装成可编辑的输入框。真正用于设置备注的复选框保留在商家表单中，并控制预览区域是否显示。

## SumUp Circuit 设计系统 React 与 CSS 集成

### 引入公开的同级依赖和两个样式表

入口使用 React 18 和固定版本的 Circuit 组件，并按照软件包要求引入令牌、图标、国际化和 Emotion 同级依赖。代码显式导入 light.css 和 styles.css，不依赖未公开说明的 provider 或隐藏的项目级主题设置。

- @sumup/circuit-ui 8.15.0 组件样式。

- @sumup/design-tokens 7.3.1 完整浅色主题。

- React 18 以及组件包声明的同级依赖。

### 仅在真实应用中添加支付服务

生产级支付流程需要单独接入经过授权的服务、验证币种处理，并处理服务器响应。此示例有意止步于核对本地呈现。复用此示例时请保留相关说明，直到真正的交易流程投入使用。

## SumUp Circuit 设计系统组件示例

### main.jsx — 完整可运行示例

编辑商品并查看顾客侧预览。下方源代码是完整的 React 入口，包含所有状态和验证逻辑。

```
import React, { useState, useRef } from "react";
import { createRoot } from "react-dom/client";
import { Input, Checkbox, Button, Headline, Body } from "@sumup/circuit-ui";
import "@sumup/design-tokens/light.css";
import "@sumup/circuit-ui/styles.css";
import "./circuit.css";
function App() {
  const [name, setName] = useState("Studio print"),
    [price, setPrice] = useState("24.00"),
    [note, setNote] = useState(true),
    [errors, setErrors] = useState({}),
    [review, setReview] = useState(null);
  const nameRef = useRef(),
    priceRef = useRef();
  const valid = /^\d{1,5}(\.\d{1,2})?$/.test(price) && Number(price) > 0;
  const amount = valid
    ? new Intl.NumberFormat("en-IE", {
        style: "currency",
        currency: "EUR",
      }).format(Number(price))
    : "—";
  function change(fn) {
    fn();
    setReview(null);
    setErrors({});
  }
  function submit(e) {
    e.preventDefault();
    const next = {};
    if (!name.trim()) next.name = "Enter an item name.";
    if (!valid)
      next.price = "Use a positive amount with up to two decimal places.";
    setErrors(next);
    if (next.name) {
      nameRef.current.focus();
      return;
    }
    if (next.price) {
      priceRef.current.focus();
      return;
    }
    setReview({ name: name.trim(), amount, note });
  }
  return (
    <main className="circuit-example">
      <header>
        <p className="eyebrow">MERCHANT TOOLS / LOCAL SPECIMEN</p>
        <Headline as="h1">Prepare a simple item</Headline>
        <Body>
          Review how an item could appear to a customer. This example never
          creates a payment link.
        </Body>
      </header>
      <div className="circuit-grid">
        <form onSubmit={submit} noValidate>
          <h2>Item details</h2>
          <Input
            ref={nameRef}
            label="Item name"
            required
            value={name}
            maxLength={80}
            invalid={!!errors.name}
            validationHint={
              errors.name || "Use a short name the customer can recognize."
            }
            onChange={(e) => change(() => setName(e.target.value))}
          />
          <Input
            ref={priceRef}
            label="Price in EUR"
            required
            inputMode="decimal"
            value={price}
            invalid={!!errors.price}
            validationHint={
              errors.price || "Example amount only; no charge will be made."
            }
            onChange={(e) => change(() => setPrice(e.target.value))}
          />
          <Checkbox
            label="Show a customer note field"
            checked={note}
            onChange={(e) => change(() => setNote(e.target.checked))}
          />
          <Button variant="primary" type="submit" stretch>
            Review item locally
          </Button>
          {review && (
            <div className="review" role="status">
              <strong>Item reviewed</strong>
              <p>
                {review.name} · {review.amount}
              </p>
              <small>
                Local snapshot only. No payment link or charge created.
              </small>
            </div>
          )}
        </form>
        <section className="preview-surface" aria-labelledby="preview-heading">
          <h2 id="preview-heading">Customer-side preview</h2>
          <div className="receipt">
            <div className="shop-mark" aria-hidden="true">
              N
            </div>
            <p className="shop-name">Northstar Studio</p>
            <div
              className="print-art"
              role="img"
              aria-label="Original geometric print illustration"
            >
              <i></i>
              <b></b>
              <span></span>
            </div>
            <h3>{name.trim() || "Your item name"}</h3>
            <p className="receipt-price">{amount}</p>
            {note && (
              <div className="note-placeholder">
                <span>Customer note</span>
                <p>A note field would appear here.</p>
              </div>
            )}
            <div className="preview-action">
              Preview only · checkout unavailable
            </div>
          </div>
        </section>
      </div>
      <footer>
        Native Circuit UI 8.15.0 components and design tokens 7.3.1. Original
        item editor and illustration by OpenDesign.
      </footer>
    </main>
  );
}
createRoot(document.getElementById("root")).render(<App />);
```

代码参考

- 创建 Vite React 项目，保留 <div id="root"></div>，并将以下内容保存为 src/main.jsx。

### circuit.css — 原始响应式布局

围绕原生组件构建的应用布局。

```
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: var(--cui-bg-normal);
  color: var(--cui-fg-normal);
  font: 16px/1.5 var(--cui-font-stack-default);
}
.circuit-example {
  padding: 32px;
}
.circuit-example header {
  max-width: 720px;
  margin-bottom: 32px;
}
.eyebrow {
  font-size: 12px;
  letter-spacing: 0.08em;
  margin-bottom: 12px;
  color: var(--cui-fg-subtle);
}
.circuit-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 1fr);
  gap: 48px;
}
.circuit-example h2 {
  font-size: 20px;
  font-weight: 700;
  margin: 0 0 24px;
}
.circuit-example form {
  max-width: 540px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.circuit-example form h2 {
  margin: 0;
}
.preview-surface {
  background: var(--cui-bg-subtle);
  padding: 28px;
  border-radius: 16px;
}
.preview-surface > h2 {
  font-size: 14px;
  color: var(--cui-fg-subtle);
  text-align: center;
}
.receipt {
  max-width: 340px;
  margin: auto;
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 20px #0000000d;
  padding: 24px;
}
.shop-mark {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #0f131a;
  color: #fff;
  display: grid;
  place-items: center;
  font-weight: 700;
  margin: auto;
}
.shop-name {
  text-align: center;
  font-size: 14px;
  margin: 12px 0 20px;
}
.print-art {
  height: 150px;
  background: #f5dfbb;
  position: relative;
  overflow: hidden;
  border-radius: 8px;
}
.print-art i {
  position: absolute;
  left: 24px;
  bottom: 0;
  width: 100px;
  height: 130px;
  border-radius: 60px 60px 0 0;
  background: #ce663d;
}
.print-art b {
  position: absolute;
  right: 20px;
  top: 20px;
  width: 90px;
  height: 90px;
  border-radius: 50%;
  background: #385d50;
}
.print-art span {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 24px;
  background: #202e2a;
}
.receipt h3 {
  font-size: 20px;
  line-height: 1.3;
  margin: 20px 0 8px;
  overflow-wrap: anywhere;
}
.receipt-price {
  font-size: 28px;
  font-weight: 700;
  margin: 0 0 24px;
}
.note-placeholder {
  padding: 12px;
  border: 1px solid #bfc6cf;
  border-radius: 8px;
  margin-bottom: 16px;
  font-size: 12px;
}
.note-placeholder span {
  font-weight: 700;
}
.note-placeholder p {
  margin: 4px 0 0;
  color: #4f5a65;
}
.preview-action {
  background: #e9edf2;
  text-align: center;
  border-radius: 999px;
  padding: 12px;
  font-size: 12px;
  color: #4f5a65;
}
.review {
  padding: 20px;
  background: #e7f5ef;
  border-radius: 8px;
}
.review p {
  margin: 8px 0;
}
.review small {
  font-size: 12px;
}
.circuit-example footer {
  font-size: 12px;
  color: var(--cui-fg-subtle);
  margin-top: 32px;
}
@media (max-width: 850px) {
  .circuit-grid {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .circuit-example form {
    max-width: none;
  }
}
@media (max-width: 550px) {
  .circuit-example {
    padding: 20px 16px;
  }
  .preview-surface {
    padding: 20px;
  }
  .receipt {
    padding: 20px;
  }
}
```

代码参考

- 将其保存在 main.jsx 旁边。这些自定义组合规则有别于库的设计令牌参考。

## SumUp Circuit 设计系统 AI Agent 设置

### 实现起点

已打包固定的原生示例，并在本地浏览器中完成检查。该示例仅使用本地应用状态。

```
npm create vite@latest circuit-item -- --template react
cd circuit-item
npm install
npm install react@18.3.1 react-dom@18.3.1 @sumup/circuit-ui@8.15.0 @sumup/design-tokens@7.3.1 @sumup/icons @sumup/intl@1 @emotion/react @emotion/styled @emotion/is-prop-valid
# Replace src/main.jsx and add src/circuit.css.
npm run dev
```

### 适用于你项目的提示词

```
使用固定版本的原生控件和两个必需的样式表，构建 Circuit 商品编辑器。验证完整的金额字符串，保留币种格式，编辑后清除过期的本地核对结果，并确保顾客侧预览与真实结账流程明确区分。使用原始插图和准确的语义令牌角色。
```

## SumUp Circuit 设计系统：来源与范围

OpenDesign 针对固定版本 Circuit 包的应用指南。插图、金额和项目均为示例。此本地编辑器不会创建付款链接、发起扣款或执行任何 SumUp 账户操作；当前上游包使用更新后的 @sumup-oss 命名空间。

- Circuit 文档 当前组件与集成指南。

- 固定浅色主题设计令牌 本示例所用的精确值。

- Circuit 来源 包版本、组件属性与当前命名空间。

## SumUp Circuit 设计系统相关资源

### TDesign

使用 TDesign React 构建紧凑的 API 政策编辑器：设置具体的主题角色、两像素尺寸比例、Form 字段注册，以及带类型的 Table 组合。

### Thumbtack Thumbprint

使用 Thumbprint 文档中记录的 v2 基础组件，构建服务发现界面：紧凑的服务提供方卡片、清晰易读的对比详情、明确的选择状态，以及常驻的候选清单。

### Twilio Paste

使用 Paste 的主题与按钮规范撰写通知草稿，采用经过验证的间距比例，并清楚区分编辑、审核和实际发送消息的状态。


- [实现参考](https://circuit.sumup.com/)
- [来源](https://cdn.jsdelivr.net/npm/@sumup/design-tokens@7.3.1/light.css)
- [来源](https://github.com/sumup-oss/circuit-ui)
- [代码参考](https://circuit.sumup.com/)
- [Circuit 文档 当前组件与集成指南。](https://circuit.sumup.com/)
- [固定浅色主题设计令牌 本示例所用的精确值。](https://cdn.jsdelivr.net/npm/@sumup/design-tokens@7.3.1/light.css)
- [Circuit 来源 包版本、组件属性与当前命名空间。](https://github.com/sumup-oss/circuit-ui)
