データプラットフォーム · 新しいリソース

Segment Evergreen デザインシステム.

実際のテーマ値、入力を制御したタグ追加、ルールとのリアルタイム照合、空の状態からの復帰手段、ブラウザー標準の確認ダイアログを備えた Evergreen の参考実装です。

OpenDesign ガイド8章36件のリファレンス項目オリジナルアプリケーションガイド
01

Segment Evergreen デザインシステムのインターフェースプレビュー

プランと必須タグを組み合わせ、一致するレコードを確認し、該当なしの場合にリセットして、ローカルのスナップショットを確認します。

ローカルのオーディエンスを作成して確認完全な例を開く
ネイティブの Evergreen 7.1.9 React コンポーネントと defaultTheme、オリジナルのローカルなオーディエンスルール。 · 実装リファレンス
02

Segment Evergreen デザインシステムのデザインリファレンス

ネイティブの Evergreen 7.1.9 React コンポーネントと 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 の 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 は、それぞれ8pxと4px刻みの値を表します。コンポーネント例では、パネル見出しの間隔に majorScale(3) を使って24pxを指定し、レスポンシブなレイアウトはカスタムスタイルシートで調整しています。この違いを明確にしましょう。グリッドのブレークポイントはアプリケーション側の判断であり、余白関数によって決まるものではありません。

モバイルでもフィールド間の関係を保つ

Segment Evergreen デザインシステムの制御タグと一致条件

値を受け入れる前に正規化する

onChange ハンドラーは文字列の前後の空白を取り除いて小文字に変換し、重複を削除したうえで、異なるタグが4つを超える場合は拒否します。追加が拒否されたときに false を返すと、入力欄の値が維持されます。この方針はオーディエンスを扱うアプリケーション側の責務です。TagInput は制御された操作を提供しますが、業務上の上限は決めません。

すべてのタグを条件にする場合は意図を明確にする

レコードは、選択したプランと必須タグすべてに一致する必要があります。タグをクリアするとタグの条件が解除され、「すべて」を選ぶとプランの条件が解除されます。現在の結果件数は、画面に表示するものと同じフィルタリング済み配列から算出されるため、件数と表示カードが食い違いません。

Segment Evergreen デザインシステムのレビュー用ダイアログとローカルスナップショット

保存前に現在のルールを確認する

ダイアログを開くには、オーディエンス名が入力されている必要があります。ダイアログには名前、件数、プラン、タグが再表示され、その後に「ローカルに保存」が提示されます。「本当によろしいですか?」とだけ尋ねる空疎な確認ではなく、具体的なスナップショットを確認できるネイティブのオーバーレイを示す手順です。

ルールが変更されたら以前の成功状態を無効にする

名前、プラン、タグを変更すると、保存済みスナップショットの表示がクリアされます。これにより、以前のローカル保存が新しく編集したルールの確認だと誤認されるのを防ぎます。このアプリケーションは、スナップショットを Segment のオーディエンスとして説明したり、顧客プラットフォームにリクエストを送信したりしません。

Segment Evergreen デザインシステムのキーボード入力とダイアログ復帰

タグ、結果、レビューを一連の流れでテストする

説明されているキーでタグを追加でき、id によって入力欄に表示ラベルが関連付けられています。タグ数の上限によって追加が拒否されると、ネイティブの危険アラートが表示されます。レビューには、フォーカス管理を自作したオーバーレイではなく、ネイティブ Dialog のフォーカス動作を使用しています。

  1. newsletter を最初の有効なタグに追加し、件数を比較します。
  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 デザインシステムのエージェント設定

実装の出発点

バージョンを固定したネイティブのサンプルをバンドルし、ローカルのブラウザーで動作を確認しました。このサンプルでは、アプリケーションのローカルな状態だけを使用します。

シェル
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 エージェント用プロンプト
実際のテーマと制御された TagInput を使って、Evergreen のオーディエンスエディターを構築します。タグを正規化して重複を除き、すべてのタグを条件とすることを明示し、結果と件数の整合性を保ち、ネイティブの Dialog で内容を確認し、ルールの変更時には古い成功状態をクリアしてください。Segment サービスとの接続をうたわないでください。
05

Segment Evergreen デザインシステムの参照元と適用範囲

Evergreen 7.1.9 向けの OpenDesign アプリケーションガイドです。架空の5件のレコードを使って、ローカルでのフィルタリングとスナップショットのレビューを説明します。Segment の顧客データやオーディエンス API には接続しません。