고객 서비스 소프트웨어 · 새 리소스

Zendesk Garden 디자인 시스템.

실제 테마 별칭, 타이포그래피 및 둥근 모서리 크기 체계, 완성된 답장 작성기, 표시 여부를 고려하는 로컬 임시 저장, 입력란 복구 기능을 다루는 고객 지원 업무용 Garden 가이드입니다.

OpenDesign 가이드8개 챕터참고 항목 42개독자적 적용 가이드
01

Zendesk Garden 디자인 시스템 인터페이스 미리보기

답변 표시 여부를 전환하고, 짧은 초안을 검증하고, 로컬 스냅샷을 저장한 다음 다음 초안을 비우세요.

고객 맥락을 확인하며 답변 초안을 작성하세요전체 예시 열기
네이티브 Garden 9.16.1 React ThemeProvider, Buttons 및 Forms, 독자적인 로컬 초안 작성 워크플로. · 구현 레퍼런스
02

Zendesk Garden 디자인 시스템 디자인 참고 자료

네이티브 Garden 9.16.1 React ThemeProvider, Buttons 및 Forms, 독자적인 로컬 초안 작성 워크플로.

모서리 반경 제어

출처
이름 / 규칙값 / 구성용도
borderRadii.sm2px컴포넌트 역할에 따라 모양을 선택하세요. 기본 중간 모서리는 알약형이 아닙니다.
borderRadii.md4px컴포넌트 역할에 따라 모양을 선택하세요. 기본 중간 모서리는 알약형이 아닙니다.
borderRadii.lg8px컴포넌트 역할에 따라 모양을 선택하세요. 기본 중간 모서리는 알약형이 아닙니다.

반응형 중단점

출처
이름 / 규칙값 / 구성용도
breakpoints.xs0px테마의 중단점이며, 애플리케이션의 모든 영역이 각 단계에서 전환된다는 뜻은 아닙니다.
breakpoints.sm576px테마의 중단점이며, 애플리케이션의 모든 영역이 각 단계에서 전환된다는 뜻은 아닙니다.
breakpoints.md768px테마의 중단점이며, 애플리케이션의 모든 영역이 각 단계에서 전환된다는 뜻은 아닙니다.
breakpoints.lg992px테마의 중단점이며, 애플리케이션의 모든 영역이 각 단계에서 전환된다는 뜻은 아닙니다.
breakpoints.xl1200px테마의 중단점이며, 애플리케이션의 모든 영역이 각 단계에서 전환된다는 뜻은 아닙니다.

글자 크기

출처
이름 / 규칙값 / 구성용도
fontSizes.xs10px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.sm12px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.md14px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.lg18px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.xl22px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.xxl26px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.
fontSizes.xxxl36px본문, 레이블, 메타데이터, 제목 역할을 각각 구분해 유지합니다.

줄 높이

출처
이름 / 규칙값 / 구성용도
lineHeights.sm16px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.
lineHeights.md20px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.
lineHeights.lg24px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.
lineHeights.xl28px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.
lineHeights.xxl32px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.
lineHeights.xxxl44px텍스트 역할과 함께 사용하세요. 모든 줄 높이를 글꼴 크기에서 계산하지 마세요.

간격 단계

출처
이름 / 규칙값 / 구성용도
space.base4일관된 리듬을 위한 테마 간격 항목입니다.
space.xxs4px일관된 리듬을 위한 테마 간격 항목입니다.
space.xs8px일관된 리듬을 위한 테마 간격 항목입니다.
space.sm12px일관된 리듬을 위한 테마 간격 항목입니다.
space.md20px일관된 리듬을 위한 테마 간격 항목입니다.
space.lg32px일관된 리듬을 위한 테마 간격 항목입니다.
space.xl40px일관된 리듬을 위한 테마 간격 항목입니다.
space.xxl48px일관된 리듬을 위한 테마 간격 항목입니다.

밝은 테마의 의미 기반 역할 매핑

출처
이름 / 규칙값 / 구성용도
colors.variables.light.background.defaultpalette.white테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.background.subtleneutralHue.100테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.background.dangerdangerHue.100테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.background.primaryEmphasisprimaryHue.700테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.foreground.defaultneutralHue.900테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.foreground.subtleneutralHue.700테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.foreground.onEmphasispalette.white테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.foreground.primaryprimaryHue.700테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.foreground.dangerdangerHue.700테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.border.defaultneutralHue.300테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.border.subtleneutralHue.200테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.border.dangerdangerHue.300테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
colors.variables.light.border.primaryEmphasisprimaryHue.700테마 역할 별칭입니다. 별칭을 CSS 색상으로 취급하지 말고, 선택한 색조와 팔레트를 통해 해석하세요.
03

Zendesk Garden 디자인 시스템 전체 가이드

실제 인터페이스를 위해 이 시스템을 선택하고 적용하고 검토하는 실용 가이드.

DESIGN-zendesk-garden.md

완전한 안내와 구현 시작점, 소스 링크.

가이드 다운로드

Zendesk Garden 디자인 시스템 지원 워크스페이스 구조

고객 요청을 계속 확인할 수 있게 하세요

지원 메시지 작성기에는 세련됐지만 엉뚱한 답변을 피할 수 있도록 충분한 맥락이 필요합니다. 이 예시에서는 요청자, 조직, 주제와 함께 고객 요청을 초안 위에 배치합니다. 화면을 전환하지 않고도 새 문구와 원래 질문을 비교할 수 있도록 인터페이스를 적절히 밀도 있게 구성했습니다.

이 OpenDesign 가이드에서

이력과 현재 초안을 분리하세요

저장된 로컬 항목은 명시적인 공개 범위 라벨과 함께 작성기 위에 표시됩니다. 현재 초안은 기본 Textarea에 남아 있습니다. 저장하면 스냅샷이 추가되고 해당 필드가 비워지므로 이후 수정으로 이전에 저장한 메모가 조용히 바뀌지 않습니다.

Zendesk Garden 디자인 시스템 테마 역할과 팔레트

색상을 복사하기 전에 별칭을 확인하세요

Garden 9 DEFAULT_THEME은 시맨틱 역할을 색상 계열 및 번호가 매겨진 팔레트 값에 연결합니다. background.primaryEmphasis와 같은 역할은 리터럴 16진수 값이 아닙니다. 참조 표에 이러한 별칭을 유지하면 사용자 지정 테마에서 기본 색상 계열을 바꾸더라도 기본 컨트롤에 파란색 값이 하드코딩된 채 남지 않습니다.

직접 작성한 영역도 프로바이더와 호환되게 하세요

ThemeProvider는 기본 컴포넌트 스타일을 제공합니다. 이 예시는 DEFAULT_THEME과 이에 어울리는 밝은 사용자 지정 워크스페이스를 사용하며, 주변 배경만 바꾸는 가짜 다크 모드 전환 기능은 추가하지 않습니다. 색상 구성표를 지원하려면 프로바이더와 함께 사용자 지정 메시지 및 사이드바 색상도 변환하세요.

Zendesk Garden 디자인 시스템 타이포그래피와 밀도

작업용 14px 텍스트의 가독성을 유지하세요

기본 테마는 14px 중간 굵기 역할과 20px 중간 행간을 제공하며, 12px 보조 텍스트와 18px 제목 역할도 별도로 제공합니다. 예시에서는 티켓 제목에 26px를 사용하고 맥락을 설명하는 메타데이터는 더 작게 표시합니다. 원래 고객 메시지와 저장된 메모는 지나치게 압축된 라벨이 아니라 일반 본문 텍스트로 유지됩니다.

긴 답변이 자연스럽게 줄바꿈되게 하세요

저장된 텍스트는 줄바꿈을 유지하고 긴 단어도 줄바꿈합니다. 따라서 모바일 화면이 좁아도 메모 전체를 볼 수 있고 가로로 스크롤할 필요가 없습니다. 작성기는 Textarea를 통해 세로로 확장되며, 메시지 이력 전체를 내부 스크롤 영역으로 만들지 않습니다.

Zendesk Garden 디자인 시스템 필드와 버튼 구성

라벨, 안내문, 오류 메시지를 하나로 연결하세요

기본 Field는 Label, Hint, Textarea 및 조건부 Message를 그룹으로 묶습니다. 유효성 검사 prop은 직접 정의한 최소 길이 규칙을 충족하지 못할 때 Textarea의 모양을 바꿉니다. 이는 오류 색상을 필드 밖의 관련 없는 문단에 적용하는 대신 컴포넌트 구성을 따릅니다.

공개 범위를 별도 컨트롤로 설정하세요

Zendesk Garden 디자인 시스템 초안 상태와 로컬 이력

메시지를 추가하기 전에 유효성을 검사하세요

애플리케이션은 공백을 제거한 뒤 최소 12자 이상이어야 합니다. 제출에 실패하면 초안은 그대로 유지되고 필드 메시지가 표시되며 Textarea에 포커스가 이동합니다. 편집하면 이전 오류가 지워지고, 다시 제출할 때 현재 텍스트를 검사합니다. 이 규칙은 애플리케이션 동작이며, 문서화되지 않은 Garden 기본값이 아닙니다.

내부 메모를 시각적으로 구분하세요

내부 항목은 따뜻한 배경색을 사용하고 내부 메모 초안이라는 라벨을 표시합니다. 공개 항목에는 일반 표면 색상을 사용합니다. 두 항목 모두 전송되지 않았음을 명시합니다. 이 구분은 공개 범위의 맥락을 보여 주면서 실제 Zendesk 대화를 모방하거나 다른 사람이 내용을 받았다는 인상을 주지 않습니다.

Zendesk Garden 디자인 시스템 반응형 작업 영역

주요 작업 영역 다음에 맥락을 배치하세요

애플리케이션의 중단점에서는 티켓 맥락이 대화 및 작성기 아래로 이동합니다. 기반 문서의 순서는 그대로 유지됩니다. 휴대전화에서는 공개 범위 컨트롤이 사용 가능한 너비 전체를 차지하고 작업 버튼 행은 줄바꿈되어 버튼 라벨이 눌려 보이지 않습니다.

적당한 둥근 모서리와 얇은 구분선을 사용하세요

기본 컨트롤에는 2px, 4px, 8px 반경 척도의 값이 사용됩니다. 원본 메시지 패널은 은은한 테두리와 4px 모서리를 사용합니다. 시각적 강조는 모든 메시지에 그림자를 넣는 대신 현재 작업과 유효성 검사 상태에 적용하세요.

Zendesk Garden 디자인 시스템 유효성 검사와 키보드 포커스 복구

초안 작성 흐름 전체를 점검하세요

유효하지 않은 초안을 제출한 뒤, 입력, 공개 범위 변경, 저장, 지우기를 차례로 수행하며 인터페이스를 계속 이해하기 쉬운 상태로 유지하는지 확인하는 것이 좋습니다. 소스는 오류 또는 지우기 작업 후 포커스를 복원하기 위해 React ref를 사용합니다. 결과 개수는 상태 영역에서 알립니다.

  1. 짧은 초안을 제출하고 필드 오류를 확인하세요.
  2. 내부 메모를 선택하고, 유용한 메모를 작성한 다음 저장하세요.
  3. 나중에 작성한 초안을 지운 뒤에도 저장된 로컬 스냅샷이 남아 있는지 확인하세요.

안정적인 읽기 순서를 유지하세요

요청이 초안보다 먼저 표시되고 새로 저장된 로컬 스냅샷은 시간순으로 유지됩니다. 기본 버튼은 작업을 텍스트로 표시합니다. 작성기 안에 키보드 단축키나 자동 전송 동작이 숨겨져 있지 않으며, 공개 범위 선택은 라벨이 있는 Select로 제공됩니다.

Zendesk Garden 디자인 시스템 React 통합

호환되는 패키지 버전을 설치하세요

테마, 버튼, 폼 패키지를 9.16.1로 고정하고 styled-components와 함께 React 18을 사용합니다. Garden 패키지의 이전 버전을 섞으면 테마에 대한 기대가 서로 맞지 않을 수 있습니다. 전체 JSX 진입점에는 프로바이더, 필드 구성, 로컬 상태가 포함되며, 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와 같은 위치에 저장하세요. 이 사용자 지정 레이아웃 규칙은 라이브러리 토큰 참조와 구분됩니다.
04

Zendesk Garden 디자인 시스템 에이전트 설정

구현 시작점

고정된 기본 예제를 번들링한 뒤 로컬 브라우저에서 확인했습니다. 이 예제는 로컬 애플리케이션 상태만 사용합니다.

sh
npm create vite@latest garden-draft -- --template react
cd garden-draft
npm install
npm install [email protected] [email protected] styled-components @zendeskgarden/[email protected] @zendeskgarden/[email protected] @zendeskgarden/[email protected]
# Replace src/main.jsx and add src/garden.css.
npm run dev

프로젝트를 위한 프롬프트

에이전트 프롬프트
기본 Field/Label/Hint/Textarea/Message 조합으로 Garden 지원 메시지 작성기를 만드세요. 저장되는 각 임시 저장본에 표시 상태를 유지하고, 내용을 추가하기 전에 유효성을 검사하며, 고객 정보를 보존하고, 로컬 저장과 메시지 전송을 구분하세요. 정확한 테마 역할과 전체 소스는 여기에서 확인할 수 있습니다.
05

Zendesk Garden 디자인 시스템 소스 및 범위

OpenDesign 애플리케이션 가이드. 기본 Garden 컴포넌트로 예시 화면을 렌더링합니다. 가상의 티켓 내용과 로컬 임시 저장본은 Zendesk에 연결되지 않으며 메시지를 전송하지 않습니다.