01Zendesk Garden 设计系统界面预览
切换回复可见性,检查简短草稿,保存本地快照,并清空后续草稿。
原生 Garden 9.16.1 React ThemeProvider、Buttons 和 Forms;原创本地起草工作流。·
实现参考 02Zendesk Garden 设计系统设计参考
原生 Garden 9.16.1 React ThemeProvider、Buttons 和 Forms;原创本地起草工作流。
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
borderRadii.sm | 2px | 根据组件角色选择形状;默认的中等圆角并非胶囊形。 |
borderRadii.md | 4px | 根据组件角色选择形状;默认的中等圆角并非胶囊形。 |
borderRadii.lg | 8px | 根据组件角色选择形状;默认的中等圆角并非胶囊形。 |
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
breakpoints.xs | 0px | 这些是主题断点,并不意味着应用会在每个断点都切换所有区域。 |
breakpoints.sm | 576px | 这些是主题断点,并不意味着应用会在每个断点都切换所有区域。 |
breakpoints.md | 768px | 这些是主题断点,并不意味着应用会在每个断点都切换所有区域。 |
breakpoints.lg | 992px | 这些是主题断点,并不意味着应用会在每个断点都切换所有区域。 |
breakpoints.xl | 1200px | 这些是主题断点,并不意味着应用会在每个断点都切换所有区域。 |
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
fontSizes.xs | 10px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.sm | 12px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.md | 14px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.lg | 18px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.xl | 22px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.xxl | 26px | 保留正文、标签、元数据与标题各自的层级。 |
fontSizes.xxxl | 36px | 保留正文、标签、元数据与标题各自的层级。 |
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
lineHeights.sm | 16px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
lineHeights.md | 20px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
lineHeights.lg | 24px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
lineHeights.xl | 28px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
lineHeights.xxl | 32px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
lineHeights.xxxl | 44px | 与文本角色配合使用;不要仅根据字号推导所有行高。 |
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
space.base | 4 | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.xxs | 4px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.xs | 8px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.sm | 12px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.md | 20px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.lg | 32px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.xl | 40px | 主题中的间距值,可用于构建节奏稳定的布局。 |
space.xxl | 48px | 主题中的间距值,可用于构建节奏稳定的布局。 |
| 名称 / 规则 | 值 / 配置 | 用途 |
|---|
colors.variables.light.background.default | palette.white | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.background.subtle | neutralHue.100 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.background.danger | dangerHue.100 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.background.primaryEmphasis | primaryHue.700 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.foreground.default | neutralHue.900 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.foreground.subtle | neutralHue.700 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.foreground.onEmphasis | palette.white | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.foreground.primary | primaryHue.700 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.foreground.danger | dangerHue.700 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.border.default | neutralHue.300 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.border.subtle | neutralHue.200 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.border.danger | dangerHue.300 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
colors.variables.light.border.primaryEmphasis | primaryHue.700 | 主题角色别名;应根据所选色相和调色板解析,而不要将别名视为 CSS 颜色值。 |
03Zendesk Garden 设计系统完整指南
实用指南,介绍如何为真实界面选择、应用和评审此系统。
DESIGN-zendesk-garden.md完整指导、实现起点和源代码链接。
下载指南Zendesk Garden 设计系统支持工作区结构
让客户请求始终清晰可见
支持回复编辑器需要展示足够的背景信息,避免回复虽措辞得体却答非所问。此示例将客户请求置于草稿上方,并在旁边显示请求者、组织和主题。界面信息足够紧凑,用户无需切换屏幕即可对照新文本与原始问题。
将历史记录与当前草稿分开
已保存的本地条目显示在编辑器上方,并明确标注其可见性。当前草稿保留在原生 Textarea 中。保存时会新增一份快照并清空该字段,因此后续编辑不会悄然改写先前保存的记录。
Zendesk Garden 设计系统主题角色与配色
复制颜色前,先查看别名
Garden 9 DEFAULT_THEME 会将语义角色映射到色相系列和编号调色板值。background.primaryEmphasis 这样的角色并不是字面意义上的十六进制值。参考表保留了这些别名,使自定义主题能够更改主色相,同时避免原生控件中残留硬编码的蓝色。
确保自定义区域与 Provider 兼容
ThemeProvider 提供原生组件样式。此示例使用 DEFAULT_THEME 和与之匹配的浅色自定义工作区;它没有添加一个只改变外围背景的伪深色模式开关。如需支持 color-scheme,请同时转换自定义消息和侧边栏的颜色,并保持与 Provider 一致。
Zendesk Garden 设计系统排版与密度
确保 14px 的工作区文本清晰易读
默认主题提供 14px 中等字重角色和 20px 行高,并分别设有 12px 辅助文本和 18px 标题角色。示例使用 26px 的工单标题和更小的上下文元数据。客户原始消息和已保存的备注使用常规正文文本,而非压缩后的标签样式。
让较长的回复自然换行
保存的文本会保留换行,并在长词处换行。因此,在较窄的手机屏幕上也能显示完整备注,无需横向滚动。编辑器通过文本区域纵向扩展,而不是把整个消息历史变成内部滚动区域。
Zendesk Garden 设计系统中的字段与按钮组合
将标签、提示和错误信息关联起来
原生 Field 组件将 Label、Hint、Textarea 和条件显示的 Message 组合在一起。当手动设定的最小长度规则未通过时,validation 属性会改变文本区域的外观。这遵循组件组合方式,而不是将错误颜色应用于字段之外、不相关的段落。
为可见范围设置独立控件
Zendesk Garden 设计系统中的草稿状态与本地历史记录
追加消息前先进行验证
应用要求修剪空白后的文本至少包含十二个字符。提交失败时,草稿会保留,同时显示字段提示并将焦点移至文本区域。编辑内容后,过时的错误会清除;再次提交时会重新检查当前文本。这是应用行为,并非 Garden 未文档化的默认行为。
让内部备注在视觉上有所区分
内部条目采用温暖的背景色,并明确标注为“内部备注草稿”。公开条目使用常规表面。两者都会明确显示“未发送”。这种区分展示了可见范围的上下文,同时避免模仿实时 Zendesk 对话,或让人误以为内容已发送给他人。
Zendesk Garden 设计系统中的响应式工作区域
将上下文区域移到主要任务下方
达到应用断点时,工单上下文会移到对话和编辑器下方。底层文档的顺序保持不变。在手机上,可见范围控件会占满可用宽度,操作行也会换行,避免按钮标签被挤压。
使用适中的圆角和细分隔线
原生控件采用 2px、4px 和 8px 的圆角比例。原始消息面板使用 4px 圆角和细微边框。较强的视觉强调应留给当前操作和验证状态,而不是为每条消息都添加阴影。
Zendesk Garden 设计系统中的验证与键盘操作恢复
完整测试草稿流程
有效的检查应从无效提交开始,然后确认输入、切换可见范围、保存和清除操作都能让界面保持清晰易懂。源代码使用 React ref,在发生错误或执行清除操作后恢复焦点。结果数量会通过状态区域播报。
- 提交一条简短草稿,并查看字段错误提示。
- 选择“内部备注”,输入有用的备注,然后保存。
- 清除后续草稿,并确认已保存的本地快照仍然保留。
保持稳定的阅读顺序
请求内容位于草稿之前,新保存的本地快照则按时间顺序排列。原生按钮会以文本说明其操作。编辑器中没有隐藏键盘快捷键或自动发送行为,可见范围选择始终是带标签的下拉框。
Zendesk Garden 设计系统的 React 集成
安装版本相匹配的软件包
此示例将主题、按钮和表单软件包固定为 9.16.1,并使用 React 18 和 styled-components。混用较旧版本的 Garden 软件包可能导致主题预期不一致。完整的 JSX 入口文件包含 Provider、字段组合和本地状态,而 CSS 文件仅包含自定义布局。
- @zendeskgarden/react-theming 9.16.1
- @zendeskgarden/react-buttons 9.16.1
- @zendeskgarden/react-forms 9.16.1
仅补充缺失的服务层
Zendesk Garden 设计系统组件示例
main.jsx — 完整可运行示例
在查看客户上下文的同时起草回复。下方是完整的 React 入口代码,包含所有状态和验证逻辑。
jsx
import React, { useState, useRef } from "react";
import { createRoot } from "react-dom/client";
import { ThemeProvider, DEFAULT_THEME } from "@zendeskgarden/react-theming";
import { Button } from "@zendeskgarden/react-buttons";
import {
Field,
Label,
Textarea,
Hint,
Message,
Select,
} from "@zendeskgarden/react-forms";
import "./garden.css";
function App() {
const [mode, setMode] = useState("public"),
[text, setText] = useState(""),
[error, setError] = useState(false),
[items, setItems] = useState([]);
const ref = useRef();
function submit(e) {
e.preventDefault();
if (text.trim().length < 12) {
setError(true);
ref.current.focus();
return;
}
setItems([...items, { mode, text: text.trim() }]);
setText("");
setError(false);
}
return (
<ThemeProvider theme={DEFAULT_THEME}>
<main className="garden-workspace">
<header>
<span>SUPPORT WORKSPACE / FICTIONAL TICKET</span>
<h1>Help with an invoice address</h1>
<p>Ticket #1028 · Nadia Walker · Email channel</p>
</header>
<div className="garden-grid">
<section>
<div className="message customer">
<strong>Nadia Walker</strong>
<small>Customer · example message</small>
<p>
Could you update the company address on our next invoice? The
billing contact has changed.
</p>
</div>
{items.map((i, n) => (
<article className={"message " + i.mode} key={n}>
<strong>
{i.mode === "public"
? "Public reply draft"
: "Internal note draft"}
</strong>
<small>Saved locally · not sent</small>
<p>{i.text}</p>
</article>
))}
<form onSubmit={submit} noValidate>
<div className="composer-head">
<h2>Compose a draft</h2>
<Field>
<Label>Visibility</Label>
<Select
value={mode}
onChange={(e) => setMode(e.target.value)}
>
<option value="public">Public reply</option>
<option value="internal">Internal note</option>
</Select>
</Field>
</div>
<Field>
<Label>
{mode === "public" ? "Reply text" : "Internal note"}
</Label>
<Hint>
{mode === "public"
? "Prepared for the customer; this demo will not send it."
: "Internal context for the support team; saved only in this page."}
</Hint>
<Textarea
ref={ref}
value={text}
rows={5}
validation={error ? "error" : undefined}
onChange={(e) => {
setText(e.target.value);
setError(false);
}}
/>
{error && (
<Message validation="error">
Write at least 12 characters.
</Message>
)}
</Field>
<div className="composer-actions">
<span>{text.length} characters</span>
<Button
type="button"
onClick={() => {
setText("");
setError(false);
ref.current.focus();
}}
>
Clear draft
</Button>
<Button isPrimary type="submit">
Save local draft
</Button>
</div>
</form>
<p role="status">
{items.length > 0
? `${items.length} draft${items.length === 1 ? "" : "s"} saved locally. No message sent.`
: ""}
</p>
</section>
<aside>
<h2>Ticket context</h2>
<dl>
<dt>Requester</dt>
<dd>Nadia Walker</dd>
<dt>Organization</dt>
<dd>Northstar Studio</dd>
<dt>Priority</dt>
<dd>Normal</dd>
<dt>Topic</dt>
<dd>Billing details</dd>
</dl>
<div className="context-note">
<h3>Before replying</h3>
<ul>
<li>Confirm the billing contact.</li>
<li>Keep invoice identifiers out of public examples.</li>
<li>State whether the change affects future invoices.</li>
</ul>
</div>
</aside>
</div>
<footer>
Native Garden 9.16.1 React components. Original local drafting
workflow; no Zendesk connection.
</footer>
</main>
</ThemeProvider>
);
}
createRoot(document.getElementById("root")).render(<App />);
代码参考
- 创建 Vite React 项目,保留 <div id="root"></div>,并将以下内容保存为 src/main.jsx。
garden.css — 原始响应式构图
围绕原生组件构建的应用布局。
css
* {
box-sizing: border-box;
}
body {
margin: 0;
font:
14px/1.5 system-ui,
sans-serif;
color: #293239;
background: #f8f9f9;
}
.garden-workspace header {
padding: 24px 32px;
background: white;
border-bottom: 1px solid #d8dcde;
}
.garden-workspace header span {
font-size: 10px;
letter-spacing: 0.08em;
color: #5c6970;
}
.garden-workspace h1 {
font-size: 26px;
line-height: 1.3;
margin: 8px 0;
}
.garden-workspace header p {
color: #5c6970;
margin: 0;
}
.garden-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) 280px;
gap: 24px;
padding: 24px 32px;
}
.message {
padding: 20px;
background: white;
border: 1px solid #d8dcde;
border-radius: 4px;
margin-bottom: 16px;
}
.message p {
white-space: pre-wrap;
overflow-wrap: anywhere;
margin: 12px 0 0;
}
.message small {
display: block;
color: #5c6970;
margin-top: 4px;
}
.message.internal {
background: #fff8ed;
border-color: #e7ce99;
}
form {
padding: 24px;
background: white;
border: 1px solid #d8dcde;
border-radius: 4px;
}
.composer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
margin-bottom: 24px;
}
.composer-head > div {
max-width: 220px;
}
.garden-workspace h2 {
font-size: 18px;
margin: 0 0 12px;
}
.composer-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
margin-top: 20px;
}
.composer-actions span {
font-size: 12px;
color: #5c6970;
margin-right: auto;
}
.garden-grid aside {
background: white;
border: 1px solid #d8dcde;
border-radius: 4px;
padding: 20px;
align-self: start;
}
dl {
margin: 0;
}
dt {
font-size: 12px;
color: #5c6970;
margin-top: 16px;
}
dd {
margin: 4px 0 0;
font-weight: 600;
}
.context-note {
margin-top: 24px;
border-top: 1px solid #e8eaec;
padding-top: 16px;
}
.context-note h3 {
font-size: 14px;
}
.context-note ul {
padding-left: 18px;
font-size: 12px;
}
.context-note li {
margin: 8px 0;
}
footer {
padding: 16px 32px;
font-size: 12px;
color: #5c6970;
border-top: 1px solid #d8dcde;
background: white;
}
@media (max-width: 850px) {
.garden-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 550px) {
.garden-grid {
padding: 16px;
}
.garden-workspace header {
padding: 20px 16px;
}
.composer-head {
display: block;
}
.composer-head > div {
max-width: none;
}
.composer-actions {
flex-wrap: wrap;
justify-content: flex-start;
}
.composer-actions span {
width: 100%;
}
form {
padding: 16px;
}
}
代码参考
- 将其保存在 main.jsx 旁边。这些自定义组合规则有别于库的设计令牌参考。
04Zendesk Garden 设计系统 AI Agent 设置
实现起点
已打包固定的原生示例,并在本地浏览器中完成检查。该示例仅使用本地应用状态。
适用于你项目的提示词
Agent 提示词
使用原生 Field/Label/Hint/Textarea/Message 组合构建 Garden 客服回复编辑器。确保每份已保存草稿快照都保留可见性信息,追加前先验证,保留客户上下文,并明确区分本地保存和发送。此处提供准确的主题角色和完整源代码。
05Zendesk Garden 设计系统来源与范围
OpenDesign 应用指南。示例由原生 Garden 组件呈现;虚构的工单内容和本地草稿不会连接到 Zendesk,也不会发送消息。