金融科技 · 新资源

SumUp Circuit 设计系统.

一份 Circuit 指南,包含精确的语义令牌、原生校验属性、完整的商家商品编辑器和图示化的客户侧预览。

OpenDesign 指南8 个章节39 条参考条目原始应用指南
01

SumUp Circuit 设计系统界面预览

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

编辑商品并查看顾客端预览打开完整示例
原生使用 @sumup/circuit-ui 8.15.0 和 @sumup/design-tokens 7.3.1 浅色 CSS;原创本地商品编辑器。· 实现参考
02

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-bit4px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-byte8px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-kilo12px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-mega16px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-giga24px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-tera32px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-peta40px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-exa48px从锁定版本的 CSS 导出中复制的命名间距级别。
--cui-spacings-zetta56px从锁定版本的 CSS 导出中复制的命名间距级别。

圆角半径

来源
名称 / 规则值 / 配置用途
--cui-border-radius-bit4px根据控件或界面表面的用途选择形状。
--cui-border-radius-byte8px根据控件或界面表面的用途选择形状。
--cui-border-radius-kilo12px根据控件或界面表面的用途选择形状。
--cui-border-radius-mega16px根据控件或界面表面的用途选择形状。
--cui-border-radius-circle100%根据控件或界面表面的用途选择形状。
--cui-border-radius-pill999999px根据控件或界面表面的用途选择形状。

字体系列与字重

来源
名称 / 规则值 / 配置用途
--cui-font-stack-defaultaktiv-grotesk, Helvetica, Arial, system-ui, sans-serif, "Segoe UI", Roboto, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"此示例使用可用的回退字体,不会加载 Aktiv Grotesk。
--cui-font-stack-monoMenlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace此示例使用可用的回退字体,不会加载 Aktiv Grotesk。
--cui-font-weight-regular400此示例使用可用的回退字体,不会加载 Aktiv Grotesk。
--cui-font-weight-bold700此示例使用可用的回退字体,不会加载 Aktiv Grotesk。

主要操作状态

来源
名称 / 规则值 / 配置用途
--cui-bg-accent-strong#0f131a 分别保留常规、悬停、按下和禁用状态。
--cui-bg-accent-strong-hovered#3b3f46 分别保留常规、悬停、按下和禁用状态。
--cui-bg-accent-strong-pressed#6a737c 分别保留常规、悬停、按下和禁用状态。
--cui-bg-accent-strong-disabledrgba(15, 19, 26, .4)分别保留常规、悬停、按下和禁用状态。

输入框与按钮的公开属性

来源
名称 / 规则值 / 配置用途
Input.labelrequired string明确字段用途。
Input.invalidboolean原生无效样式与无障碍状态。
Input.validationHintstring在控件下方显示具体说明。
Button.variantprimary / secondary / tertiary此锁定版本中的操作层级。
Button.sizes / m版本 8.15.0 中当前使用的尺寸值。
Button.stretchboolean让本地审核操作填满父容器宽度。
03

SumUp Circuit 设计系统完整指南

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

DESIGN-sumup-circuit.md

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

下载指南

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

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

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

摘自本 OpenDesign 指南

使用原创产品插画

几何风格的印刷图案由 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 组件仍能呈现预期的层次结构,而最终字形则取决于系统中可用的字体。

让金额与币种保持在一起

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 入口,包含所有状态和验证逻辑。

jsx
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 — 原始响应式布局

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

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 旁边。这些自定义组合规则有别于库的设计令牌参考。
04

SumUp Circuit 设计系统 AI Agent 设置

实现起点

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

sh
npm create vite@latest circuit-item -- --template react
cd circuit-item
npm install
npm install [email protected] [email protected] @sumup/[email protected] @sumup/[email protected] @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

适用于你项目的提示词

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

SumUp Circuit 设计系统:来源与范围

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