데이터 플랫폼 · 새 리소스

Segment Evergreen 디자인 시스템.

실제 테마 값, 제어 가능한 태그 입력, 실시간 규칙 매칭, 빈 상태에서의 복구 경로, 네이티브 검토 대화상자가 포함된 Evergreen 참조 자료입니다.

OpenDesign 가이드8개 챕터36개 레퍼런스 항목독자적 적용 가이드
01

Segment Evergreen 디자인 시스템 인터페이스 미리보기

플랜과 필수 태그를 조합하고, 일치하는 레코드를 확인하고, 결과가 없을 때 복구한 다음 로컬 스냅샷을 검토하세요.

로컬 오디언스 구성 및 검토전체 예시 열기
기본 테마인 defaultTheme과 Evergreen 7.1.9의 기본 React 컴포넌트를 사용하며, 오디언스 규칙은 직접 작성했습니다. · 구현 레퍼런스
02

Segment Evergreen 디자인 시스템 디자인 참고 자료

기본 테마인 defaultTheme과 Evergreen 7.1.9의 기본 React 컴포넌트를 사용하며, 오디언스 규칙은 직접 작성했습니다.

모서리 반경

출처
이름 / 규칙값 / 구성용도
radii.00px인덱스형 테마 반경입니다. 중첩된 모든 상자를 점차 둥글게 만들기보다 역할에 맞는 값을 선택하세요.
radii.14px인덱스형 테마 반경입니다. 중첩된 모든 상자를 점차 둥글게 만들기보다 역할에 맞는 값을 선택하세요.
radii.28px인덱스형 테마 반경입니다. 중첩된 모든 상자를 점차 둥글게 만들기보다 역할에 맞는 값을 선택하세요.

글자 크기

출처
이름 / 규칙값 / 구성용도
fontSizes.010px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.112px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.214px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.316px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.418px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.520px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.624px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.
fontSizes.732px공개된 기본 테마에서 인덱스 방식으로 지정된 font-size 값입니다.

줄 높이

출처
이름 / 규칙값 / 구성용도
lineHeights.016px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.118px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.220px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.324px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.428px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.532px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.
lineHeights.640px모든 문단에 제목의 리듬을 적용하지 말고, 의도한 텍스트 역할에 맞춰 선택하세요.

글꼴 두께

출처
이름 / 규칙값 / 구성용도
fontWeights.light300이 테마에서 Evergreen은 semibold를 500, bold를 600으로 정의합니다. 이를 브라우저의 키워드 기본값으로 임의 대체하지 마세요.
fontWeights.normal400이 테마에서 Evergreen은 semibold를 500, bold를 600으로 정의합니다. 이를 브라우저의 키워드 기본값으로 임의 대체하지 마세요.
fontWeights.semibold500이 테마에서 Evergreen은 semibold를 500, bold를 600으로 정의합니다. 이를 브라우저의 키워드 기본값으로 임의 대체하지 마세요.
fontWeights.bold600이 테마에서 Evergreen은 semibold를 500, bold를 600으로 정의합니다. 이를 브라우저의 키워드 기본값으로 임의 대체하지 마세요.

기본 팔레트 참조값

출처
이름 / 규칙값 / 구성용도
colors.gray900#101840 기본 어두운 텍스트입니다.
colors.gray700#696f8c 보조 텍스트.
colors.gray400#d8dae5 정의된 경계선입니다.
colors.gray200#edeff5 은은한 구분선입니다.
colors.gray100#F4F5F9 절제된 배경입니다.
colors.blue500#3366FF 기본 파란색 참조값입니다.
colors.blue600#2952CC 더 강한 파란색 상호작용 참조값입니다.
colors.red500#D14343 위험/오류 참조값입니다.
colors.green500#52BD95 성공 참조값입니다.

레이아웃 함수 및 제어 입력 API

출처
이름 / 규칙값 / 구성용도
majorScale(3)24px섹션 간격에 사용하는 8픽셀 주요 간격 단위입니다.
minorScale(3)12px작은 간격 관계에 사용하는 4픽셀 보조 간격 단위입니다.
TagInput.valuescontrolled array승인된 태그는 애플리케이션에서 관리합니다.
TagInput.onChangenew array; return false to retain input태그를 승인하기 전에 추가 내용을 검증합니다.
TagInput.separatorcomma and new line by default커스텀 파서 없이 여러 태그를 허용합니다.
03

Segment Evergreen 디자인 시스템 전체 가이드

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

DESIGN-segment-evergreen.md

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

가이드 다운로드

Segment Evergreen 디자인 시스템 오디언스 빌더 구조

규칙과 그에 따른 결과를 나란히 보여주세요

이 예시에서는 규칙 패널과 일치하는 가상 레코드를 함께 보여줍니다. 플랜 선택과 필수 태그가 결과 개수와 동시에 표시되므로 규칙을 바꾸면 어떤 결과가 달라지는지 쉽게 이해할 수 있습니다. 현재 조합이 일치하는 항목이 있는지 확인하기 위해 저장하거나 대화상자를 열 필요가 없습니다.

이 OpenDesign 가이드에서

유한하고 투명한 픽스처를 사용하세요

이름이 지정된 샘플 레코드 5개에 플랜과 태그를 표시합니다. 고객 데이터 웨어하우스를 조회하거나 실제 오디언스 규모를 추정하는 것처럼 보이게 하지 않습니다. 결과가 0인 상태도 예시의 유용한 일부이며, 모든 샘플 데이터를 복원하는 작업을 제공합니다.

Segment Evergreen 디자인 시스템 테마 값과 의미

실제 기본 테마 확인

Evergreen 7.1.9는 이름이 지정된 색상 및 컴포넌트 스타일과 함께 인덱스로 관리되는 모서리 반경, 글꼴 크기, 줄 높이를 제공합니다. 표에는 내보낸 값이 그대로 표시됩니다. 특히 글꼴 두께 이름을 눈여겨볼 만합니다. semibold와 bold는 각각 500과 600에 해당해 일반적인 예상과 다릅니다.

사용자 정의 구성을 일관되게 유지

ThemeProvider가 기본 입력 필드, 태그, 배지, 버튼, 대화상자를 제공합니다. 직접 작성한 패널에도 동일한 절제된 쿨 그레이와 파란색 조합을 적용합니다. 예시에서는 범용 CSS 선택자로 기본 컴포넌트 내부 스타일을 덮어쓰지 않으므로 제공된 인터랙션 스타일이 그대로 유지됩니다.

Segment Evergreen 규칙 편집을 위한 디자인 시스템 타이포그래피

작업의 위계를 명확하게 설정

제목 역할을 구분해 오디언스 제목, 패널 제목, 개별 결과 이름을 각각 표현합니다. 보조 문구에는 태그 입력 방법과 가상 데이터의 범위를 설명합니다. 결과 제목 옆에 개수 배지를 배치하고 제목을 대체하지 않아, 사용자가 숫자를 해석하기 전에 해당 영역을 식별할 수 있습니다.

메타데이터를 읽기 쉽게 유지

플랜과 태그 정보는 규칙을 설명하는 데 도움이 되는 요소이지, 단순한 장식이 아닙니다. 좁은 화면에서는 태그가 줄바꿈되고 레코드의 텍스트와 배지가 세로로 쌓이도록 구성합니다. 모든 레이블을 지나치게 작게 만들면 사용자가 규칙을 이해하는 데 필요한 근거가 보이지 않게 됩니다.

Segment Evergreen 디자인 시스템 메이저 및 마이너 스케일

반복 가능한 간격에는 함수를 사용

majorScale과 minorScale은 각각 8픽셀과 4픽셀 단위의 간격을 표현합니다. 컴포넌트 예시에서는 majorScale(3)을 사용해 패널 제목 아래에 24px 간격을 두고, 사용자 정의 스타일시트로 반응형 구성을 처리합니다. 이 차이를 분명히 유지하세요. 그리드의 브레이크포인트는 애플리케이션에서 정하는 값이지 간격 함수의 결과가 아닙니다.

모바일에서도 필드 간 관계 유지

Segment Evergreen 제어형 태그 및 매칭을 위한 디자인 시스템

값을 추가하기 전에 정규화

onChange 핸들러는 문자열 앞뒤 공백을 제거하고 소문자로 변환한 뒤 중복을 없애며, 서로 다른 태그가 4개를 넘으면 추가를 거부합니다. 추가가 거부될 때 false를 반환하면 입력 필드의 값이 유지됩니다. 이 정책은 오디언스 애플리케이션의 책임입니다. TagInput은 제어형 입력을 제공할 뿐, 비즈니스 규칙에 따른 제한을 정하지 않습니다.

모든 태그 일치 조건을 명시적으로 적용

레코드는 선택한 플랜과 필수 태그 전체에 일치해야 합니다. 태그를 모두 지우면 태그 조건이 해제되고, 플랜을 ‘모든 플랜’으로 선택하면 플랜 조건이 해제됩니다. 화면에 표시되는 동일한 필터링 배열에서 결과 개수를 계산해 개수와 카드 내용이 서로 어긋나지 않게 합니다.

Segment Evergreen 검토 대화상자 및 로컬 스냅샷을 위한 디자인 시스템

저장 전에 현재 규칙 검토

오디언스 이름을 입력해야 대화상자를 열 수 있습니다. 대화상자에는 이름, 결과 개수, 플랜, 태그를 다시 표시한 다음 ‘로컬에 저장’ 작업을 제공합니다. ‘정말 진행하시겠습니까?’만 표시하는 빈 확인 창 대신, 구체적인 스냅샷을 기본 오버레이로 검토하는 과정을 보여 줍니다.

규칙이 바뀌면 이전 성공 상태 무효화

이름, 플랜 또는 태그를 변경하면 저장된 스냅샷 표시가 지워집니다. 따라서 이전에 로컬 저장에 성공한 상태를 새로 편집한 규칙의 확인으로 오해하지 않게 됩니다. 이 애플리케이션은 스냅샷을 Segment 오디언스라고 설명하지 않으며 고객 플랫폼에 요청을 보내지도 않습니다.

Segment Evergreen 키보드 입력 및 대화상자 복구를 위한 디자인 시스템

태그, 결과, 검토를 하나의 흐름으로 테스트

문서에 명시된 입력 키로 태그를 추가할 수 있으며, id를 통해 입력 필드에 표시 레이블을 연결합니다. 태그 수 제한으로 추가가 거부되면 기본 위험 알림을 표시합니다. 검토 화면에는 포커스를 직접 관리하지 않는 맞춤 오버레이 대신 기본 Dialog의 포커스 동작을 사용합니다.

  1. 초기 활성 태그에 뉴스레터를 추가하고 결과 개수를 비교합니다.
  2. 일치하지 않는 태그를 추가한 다음 규칙을 모두 지워 레코드 5개를 복원합니다.
  3. 검토 후 로컬에 저장하고, 규칙을 변경해 이전 성공 상태가 사라지는지 확인합니다.

현재 작업 안에서 빈 상태 복구 지원

규칙 지우기는 플랜 및 태그 조건을 모두 해제하고, 오래된 저장 상태도 숨깁니다. 그러면 결과 영역에 레코드 5개가 다시 표시됩니다. 사용자는 숨겨진 화면 이동이나 페이지 새로고침 없이 바로 편집을 계속할 수 있습니다.

Segment Evergreen 디자인 시스템 React 통합

완전한 제어형 예시 사용

기본 엔트리에서 ThemeProvider, defaultTheme, 렌더링에 사용하는 컴포넌트를 가져옵니다. 상태에는 규칙, 오류, 대화상자 표시 여부, 로컬에 저장한 스냅샷이 포함됩니다. 아래의 전체 JSX와 CSS에는 가상 샘플 데이터가 포함되어 있으므로 외부 API 없이도 결과 계산을 재현할 수 있습니다.

  • ThemeProvider에서 고정된 defaultTheme을 사용합니다.
  • TagInput 값은 애플리케이션에서 제어합니다.
  • Dialog에는 결과 목록과 동일한 규칙이 표시됩니다.

데이터 접근은 별도의 애플리케이션 계층으로 추가

Segment Evergreen 디자인 시스템 컴포넌트 예시

main.jsx — 완전히 작동하는 예제

로컬에서 오디언스를 구성하고 검토합니다. 아래 소스는 상태 관리와 유효성 검사를 모두 포함한 전체 React 엔트리입니다.

jsx
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
import {
  ThemeProvider,
  defaultTheme,
  Heading,
  Text,
  Paragraph,
  Button,
  TagInput,
  TextInputField,
  SelectField,
  Alert,
  Dialog,
  Badge,
  majorScale,
} from "evergreen-ui";
import "./evergreen.css";
const people = [
  { name: "Avery Stone", plan: "Pro", tags: ["active", "newsletter"] },
  { name: "Taylor Reed", plan: "Free", tags: ["newsletter"] },
  { name: "Jordan Park", plan: "Pro", tags: ["active"] },
  { name: "Morgan Lee", plan: "Team", tags: ["active", "newsletter"] },
  { name: "Casey Chen", plan: "Free", tags: ["trial"] },
];
function App() {
  const [tags, setTags] = useState(["active"]),
    [plan, setPlan] = useState("Any"),
    [name, setName] = useState("Active customers"),
    [error, setError] = useState(""),
    [open, setOpen] = useState(false),
    [saved, setSaved] = useState(null);
  const matches = people.filter(
    (p) =>
      (plan === "Any" || p.plan === plan) &&
      tags.every((t) => p.tags.includes(t)),
  );
  function changeTags(values) {
    const unique = [
      ...new Set(values.map((v) => v.trim().toLowerCase()).filter(Boolean)),
    ];
    if (unique.length > 4) {
      setError("Use at most four tags.");
      return false;
    }
    setTags(unique);
    setError("");
    setSaved(null);
  }
  return (
    <ThemeProvider value={defaultTheme}>
      <main className="audience">
        <header>
          <Text size={300}>WORKSPACE / AUDIENCES</Text>
          <Heading size={700} marginTop={8}>
            Build a focused audience
          </Heading>
          <Paragraph marginTop={8}>
            Combine a plan and tags against five fictional records. No customer
            data is loaded.
          </Paragraph>
        </header>
        <div className="audience-grid">
          <section className="rule-panel">
            <Heading size={500} marginBottom={majorScale(3)}>
              Audience rules
            </Heading>
            <TextInputField
              label="Audience name"
              value={name}
              onChange={(e) => {
                setName(e.target.value);
                setSaved(null);
              }}
            />
            <SelectField
              label="Plan"
              value={plan}
              onChange={(e) => {
                setPlan(e.target.value);
                setSaved(null);
              }}
            >
              <option>Any</option>
              <option>Free</option>
              <option>Pro</option>
              <option>Team</option>
            </SelectField>
            <label htmlFor="audience-tags">Required tags</label>
            <Paragraph size={300} marginBottom={8}>
              Press Enter or comma to add a tag. Every tag must match.
            </Paragraph>
            <TagInput
              values={tags}
              onChange={changeTags}
              width="100%"
              inputProps={{ id: "audience-tags", placeholder: "Add tag…" }}
            />
            <Paragraph size={300} marginTop={8}>
              Available sample tags: active, newsletter, trial.
            </Paragraph>
            {error && <Alert intent="danger" title={error} marginTop={16} />}
            <Button
              appearance="primary"
              marginTop={24}
              onClick={() => {
                if (!name.trim()) {
                  setError("Name the audience before reviewing.");
                  return;
                }
                setError("");
                setOpen(true);
              }}
            >
              Review audience
            </Button>
          </section>
          <section className="results-panel">
            <div className="results-title">
              <Heading size={500}>Matching sample records</Heading>
              <Badge color="blue">
                {matches.length} of {people.length}
              </Badge>
            </div>
            {matches.length ? (
              <div className="record-list">
                {matches.map((p) => (
                  <article key={p.name}>
                    <div>
                      <Heading size={400}>{p.name}</Heading>
                      <Text size={300}>{p.plan} plan</Text>
                    </div>
                    <div className="tag-list">
                      {p.tags.map((t) => (
                        <Badge key={t} color="neutral">
                          {t}
                        </Badge>
                      ))}
                    </div>
                  </article>
                ))}
              </div>
            ) : (
              <div className="empty">
                <Heading size={500}>No matching sample records</Heading>
                <Paragraph marginTop={8}>
                  Remove a tag or change the plan to broaden the selection.
                </Paragraph>
                <Button
                  marginTop={16}
                  onClick={() => {
                    setTags([]);
                    setPlan("Any");
                    setSaved(null);
                  }}
                >
                  Clear rules
                </Button>
              </div>
            )}
            {saved && (
              <Alert
                intent="success"
                title="Local audience saved"
                marginTop={20}
              >
                {saved.name} · {saved.count} sample records. Nothing was sent to
                Segment.
              </Alert>
            )}
          </section>
        </div>
        <Dialog
          isShown={open}
          title="Review local audience"
          onCloseComplete={() => setOpen(false)}
          confirmLabel="Save locally"
          onConfirm={() => {
            setSaved({ name: name.trim(), count: matches.length });
            setOpen(false);
          }}
          width={560}
        >
          <Paragraph>
            <strong>{name}</strong> matches {matches.length} of {people.length}{" "}
            fictional records.
          </Paragraph>
          <Paragraph marginTop={12}>
            Plan: {plan}. Required tags: {tags.join(", ") || "none"}.
          </Paragraph>
          <Paragraph marginTop={12}>
            This saves only a local snapshot; it does not create a Segment
            audience.
          </Paragraph>
        </Dialog>
        <footer>
          Native Evergreen 7.1.9 React components. Original audience logic; no
          analytics or customer service connection.
        </footer>
      </main>
    </ThemeProvider>
  );
}
createRoot(document.getElementById("root")).render(<App />);

코드 참조

  • Vite React 프로젝트를 만들고 <div id="root"></div>를 유지한 다음, 이 파일을 src/main.jsx로 불러오세요.

evergreen.css — 기존 반응형 구성

기본 구성 요소를 중심으로 구성한 애플리케이션 레이아웃입니다.

CSS
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: #f4f5f9;
  color: #101840;
  font:
    14px/1.5 system-ui,
    sans-serif;
}
.audience {
  padding: 32px;
}
.audience header {
  max-width: 800px;
  margin-bottom: 28px;
}
.audience-grid {
  display: grid;
  grid-template-columns: minmax(260px, 0.85fr) minmax(0, 1.5fr);
  gap: 24px;
}
.rule-panel,
.results-panel {
  padding: 24px;
  background: white;
  border: 1px solid #d8dae5;
  border-radius: 8px;
  min-width: 0;
}
.rule-panel > label {
  font-weight: 600;
  display: block;
  margin-bottom: 8px;
}
.results-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}
.record-list article {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 0;
  border-bottom: 1px solid #edeff5;
}
.record-list article:first-child {
  padding-top: 0;
}
.record-list article:last-child {
  border: 0;
}
.tag-list {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.empty {
  padding: 36px 12px;
  text-align: center;
  background: #f4f5f9;
  border-radius: 4px;
}
.audience footer {
  margin-top: 24px;
  font-size: 12px;
  color: #696f8c;
}
.audience input {
  max-width: 100%;
}
[role="dialog"] {
  max-width: calc(100vw - 32px);
}
@media (max-width: 850px) {
  .audience-grid {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 550px) {
  .audience {
    padding: 16px;
  }
  .rule-panel,
  .results-panel {
    padding: 20px;
  }
  .record-list article {
    align-items: flex-start;
    flex-direction: column;
  }
  .results-title {
    align-items: flex-start;
  }
}

코드 참조

  • main.jsx와 같은 위치에 저장하세요. 이 사용자 지정 레이아웃 규칙은 라이브러리 토큰 참조와 구분됩니다.
04

Segment Evergreen 디자인 시스템 AI 에이전트 설정

구현 시작점

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

sh
npm create vite@latest evergreen-audience -- --template react
cd evergreen-audience
npm install
npm install [email protected] [email protected] [email protected]
# Replace src/main.jsx and add src/evergreen.css.
npm run dev

프로젝트를 위한 프롬프트

에이전트 프롬프트
실제 테마와 제어형 TagInput을 사용해 Evergreen 오디언스 편집기를 구성합니다. 태그를 정규화하고 중복을 제거하며, 모든 태그 일치 조건을 명시하고, 결과와 개수를 일치시킵니다. 기본 Dialog에서 검토하고 규칙이 바뀌면 이전 성공 상태를 지웁니다. Segment 서비스에 연결된 것처럼 설명하지 마세요.
05

Segment Evergreen 디자인 시스템 소스 및 범위

Evergreen 7.1.9용 OpenDesign 애플리케이션 가이드입니다. 가상의 레코드 5개를 사용해 로컬 필터링과 스냅샷 검토를 보여 줍니다. Segment 고객 데이터나 오디언스 API는 연결되어 있지 않습니다.