Платформа данных · Новый ресурс

Дизайн-система Segment Evergreen.

Пример Evergreen с фактическими значениями темы, управляемым вводом тегов, динамической проверкой правил, восстановлением после пустого состояния и встроенным диалогом проверки.

Руководство OpenDesign8 глав36 справочных материаловАвторское руководство по приложению
01

Segment Evergreen Предпросмотр интерфейса дизайн-системы

Выберите план и обязательные теги, просмотрите подходящие записи, восстановите результаты после отсутствия совпадений и изучите локальный снимок данных.

Создайте и изучите локальную аудиториюОткрыть полный пример
Нативные компоненты React Evergreen 7.1.9 и defaultTheme; исходные правила локальной аудитории. · Справочник по реализации
02

Segment Evergreen Справочник по дизайн-системе

Нативные компоненты React Evergreen 7.1.9 и defaultTheme; исходные правила локальной аудитории.

Радиусы скругления

Источник
Название / правилоЗначение / конфигурацияПрименение
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 обозначает полужирное начертание значением 500, а жирное — 600; не подменяйте их без предупреждения стандартными ключевыми словами браузера.
fontWeights.normal400В этой теме Evergreen обозначает полужирное начертание значением 500, а жирное — 600; не подменяйте их без предупреждения стандартными ключевыми словами браузера.
fontWeights.semibold500В этой теме Evergreen обозначает полужирное начертание значением 500, а жирное — 600; не подменяйте их без предупреждения стандартными ключевыми словами браузера.
fontWeights.bold600В этой теме Evergreen обозначает полужирное начертание значением 500, а жирное — 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Основной шаг — восемь пикселей для интервалов между разделами.
minorScale(3)12pxДополнительный шаг — четыре пикселя для небольших интервалов.
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

Используйте ограниченный набор прозрачных тестовых данных

В примере представлены пять записей с названиями, тарифами и тегами. Страница не имитирует запрос к хранилищу данных клиентов и не оценивает размер реальной аудитории. Нулевое число результатов — полезная часть примера; предусмотрено действие, восстанавливающее полный набор данных.

Значения и семантика темы дизайн-системы Segment Evergreen

Изучите фактические значения стандартной темы

В Evergreen 7.1.9, помимо именованных цветов и стилей компонентов, заданы радиусы, размеры шрифта и высота строки с индексами. В таблице приведены именно экспортируемые значения. Особенно важно проверить названия начертаний: полужирное и жирное соответствуют значениям 500 и 600, что отличается от распространённых представлений.

Сохраняйте согласованность пользовательской композиции

ThemeProvider предоставляет стандартные поля ввода, теги, бейджи, кнопки и диалоговое окно. В оформлении панелей используется та же сдержанная гамма холодных серых и синих оттенков. Пример не переопределяет внутренние стили стандартных компонентов с помощью широких селекторов CSS, поэтому встроенное оформление состояний взаимодействия сохраняется.

Типографика для редактирования правил в дизайн-системе Segment Evergreen

Задайте понятную иерархию

Стили заголовков помогают отличить название аудитории от заголовков панелей и названий записей в результатах. Вспомогательный текст поясняет ввод тегов и вымышленный характер данных. Бейдж с числом результатов расположен рядом с заголовком списка, а не вместо него: сначала пользователь определяет нужную область, а затем читает число.

Сохраняйте читаемость метаданных

Данные о тарифе и тегах помогают понять, почему запись соответствует правилам, и служат не только украшением. На узком экране теги переносятся, а текст и бейджи записи располагаются друг под другом. Если уменьшить все подписи до крошечного размера, пользователю будет сложнее проверить правила.

Основная и дополнительная сетки дизайн-системы Segment Evergreen

Используйте функции для повторяющихся интервалов

majorScale и minorScale задают шаги в восемь и четыре пикселя. В примере компонента majorScale(3) задаёт интервал 24px между заголовками панелей, а адаптивная композиция оформлена пользовательской таблицей стилей. Разделяйте эти задачи: контрольная точка сетки — решение приложения, а не результат применения функции отступов.

Сохраняйте взаимосвязь полей на мобильных устройствах

Контролируемые теги и сопоставление в дизайн-системе Segment Evergreen

Нормализуйте значения перед добавлением

Обработчик onChange удаляет пробелы в начале и конце строк, переводит символы в нижний регистр, убирает дубликаты и отклоняет ввод, если в нём больше четырёх разных тегов. Возврат false сохраняет значение поля, если новый тег отклонён. Это правило относится к логике приложения для работы с аудиториями; TagInput обеспечивает контролируемое взаимодействие, но не задаёт бизнес-ограничения.

Осознанно используйте сопоставление по всем тегам

Запись должна соответствовать выбранному тарифу и каждому обязательному тегу. Если удалить все теги, это ограничение снимается; если выбрать Any, снимается ограничение по тарифу. Текущее число результатов вычисляется по тому же отфильтрованному массиву, который отображается на экране, поэтому число не расходится с видимыми карточками.

Диалог подтверждения и локальный снимок в дизайн-системе Segment Evergreen

Проверяйте текущие правила перед сохранением

Чтобы открыть диалоговое окно, нужно указать название аудитории. В окне повторно отображаются название, число результатов, тариф и теги, а затем предлагается сохранить данные локально. Этот дополнительный шаг показывает работу стандартного всплывающего окна с конкретным снимком настроек вместо пустого подтверждения с вопросом «Вы уверены?».

Сбрасывайте прежний статус успеха при изменении правил

Изменение названия, тарифа или тегов удаляет индикатор сохранённого снимка. Поэтому прежнее сообщение об успехе нельзя принять за подтверждение новых правил. Приложение не называет снимок аудиторией Segment и не отправляет запросы на платформу для работы с данными клиентов.

Ввод с клавиатуры и восстановление диалога в дизайн-системе Segment Evergreen

Проверьте теги, результаты и подтверждение в рамках одного сценария

Теги можно добавлять с помощью описанных клавиш ввода; доступное имя поля задаётся через id. Если превышен лимит тегов, появляется стандартное предупреждение об ошибке. Для диалога используется стандартная логика управления фокусом Dialog, а не самодельное всплывающее окно с неконтролируемым фокусом.

  1. Добавьте newsletter к начальному активному тегу и сравните число результатов.
  2. Добавьте тег, которому ничего не соответствует, а затем сбросьте правила, чтобы восстановить все пять записей.
  3. Просмотрите и сохраните данные локально, затем измените правило и убедитесь, что прежнее сообщение об успехе исчезло.

Предусмотрите восстановление после пустого результата в рамках текущей задачи

Сброс правил снимает ограничения по тарифу и тегам, а также скрывает устаревшее сообщение о сохранении. После этого в списке результатов снова появляются все пять записей. Пользователь может сразу продолжить редактирование — скрытая навигация и перезагрузка страницы не требуются.

Интеграция дизайн-системы 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 />);

Справочник по коду

  • Создайте проект React на Vite, сохраните <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

Отправная точка для реализации

Пример с закреплёнными версиями нативных компонентов собран и проверен в браузере локально. В образце используется только локальное состояние приложения.

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

Запрос для вашего проекта

Промпт для AI-агента
Создайте редактор аудитории на основе Evergreen с фактической темой и контролируемым TagInput. Нормализуйте теги и удаляйте дубликаты, явно задайте сопоставление по всем тегам, обеспечьте согласованность списка и числа результатов, просматривайте данные в стандартном Dialog и сбрасывайте устаревшее сообщение об успехе при изменении правил. Не заявляйте о подключении к сервису Segment.
05

Источники и область применения дизайн-системы Segment Evergreen

Руководство по приложению OpenDesign для Evergreen 7.1.9. Пять вымышленных записей демонстрируют локальную фильтрацию и просмотр снимка. Данные клиентов Segment и API аудитории не подключены.