01 Emplifi Soul デザインシステムのインターフェースプレビュー 全選択、不確定な選択、並べ替え、一括レビュー、キャンセルをお試しください。
Soulのデザインガイダンスと検証済みのライトモードトークンを、独自にHTML/CSSとして解釈したものです。システムフォントを代替として使用し、非公開のReactアセットは使用していません。·
実装リファレンス 02
Emplifi Soul デザインシステムのデザインリファレンス 公開されているSoulのデザインドキュメントと、公式Storybookのライブ版です。ここに示すのは名称が定められたライトモードの基本値であり、すべてのドキュメントページやパッケージリリースが同じ状況にあるという意味ではありません。プレビューではローカル環境の代替としてシステムフォントを使用します。
名前 / ルール 値 / 設定 用途 --color-layer-level-0#f3f3f5 ライトモードのサーフェスレベル。 --color-layer-level-1#ffffff ライトモードのサーフェスレベル。 --color-layer-level-2#f3f3f5 ライトモードのサーフェスレベル。 --color-layer-level-3#e7e7e7 ライトモードのサーフェスレベル。 --color-on-layer-primary#24242b 本文テキスト。 --color-on-layer-secondary#505057 補助テキスト。 Dark level 0 / level 1#111114 / #24242b暗いサーフェスは、奥行きが増すほど明るくなります。
名前 / ルール 値 / 設定 用途 --color-button-primary#0c4bff プライマリボタンの背景色。 --color-on-layer-interactive#003beb インタラクティブなテキスト。 --color-border-focus-ring#003beb フォーカスリング。 --color-table-row-odd#ffffff 奇数行とテーブルのヘッダー/フッター。 --color-table-row-even#f9f9fa 偶数行。 --color-table-row-hover#d5efff 読み込み中のフィードバック。 --color-layer-main-navigation#111114 メインナビゲーションのみ。
名前 / ルール 値 / 設定 用途 --space-13pxFoundationの間隔。 --space-26pxFoundationの間隔。 --space-39pxFoundationの間隔。 --space-412pxFoundationの間隔。 --space-518pxFoundationの間隔。 --space-624pxFoundationの間隔。 --space-730pxFoundationの間隔。 --space-836pxFoundationの間隔。 --space-948pxFoundationの間隔。 Default control height36pxコンパクトオプションは30pxです。間隔スケールと混同しないでください。
名前 / ルール 値 / 設定 用途 --border-radius-small2pxわずかに丸みをつけます。 --border-radius-medium6px標準の要素。 --border-radius-large8pxコンテナ。 --border-radius-full50%円形。 --border-radius-pill999pxピル型。
名前 / ルール 値 / 設定 用途 headline-1500 · 30px / 42pxページのメイン見出し。 headline-2500 · 20px / 30pxセクション見出し。 headline-3500 · 16px / 24pxサブセクション見出し。 paragraph-regular400 · 13px / 18px中サイズの本文。 Button label600 · 13px / 18px文頭のみ大文字にする表記。 Table numeralsTabular numbers比較可能な数値は右揃えにします。
名前 / ルール 値 / 設定 用途 Package@sbks/ds-button非公開スコープです。Buttonは名前付きエクスポートです。 variantBUTTON_VARIANT.LIGHT defaultEnumに基づく値です。TypeScriptには文字列を直接指定せず、エクスポートされたEnum定数を使用してください。 sizeBUTTON_SIZE.MEDIUM defaultMediumが基本です。 isLoadingfalse読み込み中の表示。 isSelectedfalse選択状態の外観。 isAppliedfalse現在、見えないvariantを視覚的にサポートします。 onClickMouse event handlerローカルのアプリケーション状態と接続します。 badgestring | number選択内容の適用情報。 themeInherited; light fallback暗いサーフェスにしても、すべての子要素が自動的に変わるとは限りません。
03 Emplifi Soul デザインシステム完全ガイド 実際のインターフェースで、このデザインシステムを選び、適用し、レビューするための実践ガイドです。
DESIGN-emplifi-soul.md 詳しいガイダンス、実装の出発点、ソースへのリンクをまとめています。
ガイドをダウンロード Emplifi Soul デザインシステムのデザインガイダンスと実装状況 コンポーネント名だけからパッケージを推測しないでください SoulはEmplifiのデザインシステムです。公開されているデザインリファレンスとStorybookは閲覧できますが、公式クイックスタートの利用には非公開のnpmスコープへのアクセスが必要です。ブラウザー上のサンプルは独自のHTML/CSSであり、SoulのReactはバンドルしていません。テーブルの動作はデザインガイダンスを解釈したもので、公開ページではReactは引き続き「開発予定」とされています。
提示されたReactの例では、意図的にセマンティックなテーブルと実際のSoul Buttonコントロールを使用しています。ドキュメントに記載のないDataTableエクスポートがあると主張せずに、動作するアプリケーション状態を実現しています。公開ドキュメントを閲覧できても、パッケージへのアクセス権や非公開アセットの再配布許可が得られるわけではありません。
関連する仕様を確認する
Emplifi Soul デザインシステム:行選択にとどまらないデータテーブルの仕様 ヘッダー、集計、欠損値 Soulのテーブルガイダンスでは、ヘッダー、行、セル、および任意のサマリーバーを区別しています。数値は右揃え、テキストは左揃えにします。サマリーには合計や平均を表示できるため、説明のない数値だけを示すのではなく、どの集計方法なのかをラベルで明確にする必要があります。
画像に動画の再生時間を示す場合のように、対象外の指標にはN/Aを使用します。入手できないデータや不明なデータにはem dash(—)を使用します。これらをゼロに置き換えると、サマリーや比較の解釈が変わってしまうため、ゼロには変換しないでください。
現在のサンプルでは、並べ替えと行選択を実装していますが、集計計算は行いません。 展開行と欠損データの説明は、ドキュメントで示されている拡張ポイントです。プレビューでは再現していません。 デザインリファレンスではReactテーブルは開発予定とされているため、SoulのDataTableを独自にimportするのではなく、提示コード内でテーブルのマークアップを行っています。 関連する仕様を確認する
一括モードで利用可能なアクションが変わります Soulでは、一括アクションバーをコレクションの下に配置し、一括選択中は各行のアクションを非表示にします。ヘッダーのチェックボックスには、チェック済み、未チェック、不確定の3つの状態があります。並べ替えは一度に1列のみです。
このサンプルでは、3件のレコードすべてがローカルにあり、選択範囲内にとどまります。「リーチ」ヘッダーは並べ替えの方向を示し、並べ替え後も選択されたIDは維持されます。今後、行単位のアクションを追加する場合は、バッチツールバーと競合しないよう、ドキュメントに記載された一括モードの制約に従う必要があります。
関連する仕様を確認する
Emplifi Soul デザインシステム:ニュートラルなレイヤーで奥行きをつくる 控えめなCXワークスペース Soul のライトモードでは、グレーと白の界面表面が交互に現れ、ダークモードではレイヤーが段階的に明るくなります。このサンプルでは、グレーのページ、白いテーブルの背景、コンパクトなダークナビゲーションバーを使用しています。色は装飾として各行に加えるのではなく、役割を示すために使います。
操作用の青は、レビュー操作と選択中のコントロールにのみ使用します。投稿に添えられた小さなオリジナルイラストには、装飾的な色を使います。内容のテーマを区別するためのものであり、誤って成功や危険を意味づけることはありません。
関連する仕様を確認する
Emplifi Soul デザインシステム:階層を保ちながら文字をコンパクトに 基本フォントは Inter Soul では、インターフェースに Inter、アラビア語テキストに Noto Sans Arabic、コードに Noto Sans Mono を指定しています。プレビューにはこれらのフォントファイルが含まれていないため、システムフォントを使用しています。サイズは公開されているレシピに基づいています。
30/42 のメイン見出しを中心に、13/18 の本文スタイルを構成しています。表の数値には等幅数字を使用します。マーケティング向けの大きなディスプレイ文字は使わず、投稿タイトルも画像に埋め込んだラベルではなく、読みやすいテキストとして表示します。
関連する仕様を確認する
Emplifi Soul デザインシステム:6 px のリズムとコンテナの角丸 密度の高いコントロールを整理する 基本のスペーシング単位は 6 px です。12、18、24、36 px などの値で、より大きな間隔の関係を定めます。3 px と 9 px は、ページの標準マージンではなく、コンポーネント内部のコンパクトな間隔に使用します。ドキュメントに記載されたコンテンツのパディングは、幅 1559 px 以下では 24 px、幅 1560 px 以上では 36 px です。
Soul では、スペーシングの数値と T シャツサイズによるコンポーネントサイズを分けています。medium のコントロールは --space-6 ではありません。このサンプルでは、コントロールの高さに 36 px、要素の角丸に 6 px、コンテナの角丸に 8 px を使用しています。
関連する仕様を確認する
Emplifi Soul デザインシステム:非公開ライブラリを適切に読み込む Foundation CSS を読み込んでからコンポーネントを構成する 公式のエントリーポイントでは、@sbks/ds-design-library から variables.css と default-styles.css をインポートします。Sass のエントリーポイントには mixin が含まれており、グローバルな CSS とは別です。Button は @sbks/ds-button からインポートします。
実際の Button API には isLoading、isSelected、isApplied、badge が含まれます。enum に基づく props では、TypeScript からエクスポートされた enum 定数が必要です。この例では、enum のインポートパスを推測せず、検証済みのデフォルト LIGHT バリアントを使用しています。
公式クイックスタートに従って、認可された非公開スコープへのアクセスとビルドローダーを設定します。 アプリケーションのエントリーポイントで基盤の CSS を一度だけインポートし、使用するフォントアセットを設定します。 提供されている制御可能なテーブルの例をマウントし、公開ワークフローに接続する前に、選択、キャンセル、ローカルでのレビューをテストします。 関連する仕様を確認する
Emplifi Soul デザインシステム:コンポーネントの例 完全なスタンドアロンのインタラクティブ見本 ドキュメント全体を index.html として保存し、ブラウザーで開きます。Soul のデザインガイダンスと検証済みのライトモードのトークンをもとに、独立して解釈した HTML/CSS です。システムフォントにフォールバックし、非公開の React アセットは使用しません。
html コードをコピー
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Content review</title><style>:root{--color-layer-level-0:#f3f3f5;--color-layer-level-1:#fff;--color-on-layer-primary:#24242b;--color-on-layer-secondary:#505057;--color-button-primary:#0c4bff;--space-2:6px;--space-4:12px;--space-6:24px;--border-radius-medium:6px;--border-radius-large:8px}*{box-sizing:border-box}body{margin:0;background:var(--color-layer-level-0);color:var(--color-on-layer-primary);font:13px/18px system-ui}nav{padding:15px 24px;background:#111114;color:#f9f9fa;font-size:13px;display:flex;justify-content:space-between;gap:24px}main{padding:24px}header{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;margin-bottom:24px}h1{font-size:30px;font-weight:500;line-height:42px;margin:0}p{color:#505057;margin:6px 0}small{font-size:11px}.surface{background:white;border:1px solid #ddd;border-radius:8px;overflow:hidden}.bar{padding:18px 24px;border-bottom:1px solid #ddd;display:flex;justify-content:space-between;gap:12px}table{width:100%;border-collapse:collapse;table-layout:fixed}caption{text-align:left;padding:12px 24px;background:#f9f9fa;font-size:11px;color:#505057}th,td{padding:16px 12px;text-align:left;border-bottom:1px solid #e7e7e7;font-weight:400}thead th{font-size:11px;font-weight:600;padding-top:12px;padding-bottom:12px}th:first-child,td:first-child{width:44px;text-align:center}.title{width:46%}.reach{width:15%;text-align:right;font-variant-numeric:tabular-nums}tbody tr:nth-child(even){background:#f9f9fa}tbody tr:hover{background:#d5efff}input{accent-color:#003beb;width:16px;height:16px;cursor:pointer}input:focus-visible,button:focus-visible{outline:2px solid #003beb;outline-offset:3px}button{font:600 13px/18px system-ui;border:1px solid #ccc;background:white;padding:8px 12px;border-radius:6px;min-height:36px;cursor:pointer;color:inherit}button:hover{background:#f3f3f5}#sort{border:0;background:transparent;padding:0;min-height:28px;color:#003beb}#apply{background:#0c4bff;color:white;border-color:#0c4bff}#apply:hover{background:#003beb}.name{display:flex;align-items:center;gap:12px}.art{width:52px;height:52px;flex:0 0 52px;border-radius:6px;position:relative;overflow:hidden}.field{background:#d4e5d3}.field:before{content:'';position:absolute;width:65px;height:65px;border-radius:50%;background:#7ca77a;left:15px;top:20px}.field:after{content:'';position:absolute;width:16px;height:16px;border-radius:50%;background:#fff1c5;left:8px;top:8px}.studio{background:#e6daee}.studio:before{content:'';position:absolute;width:20px;height:36px;background:#a089b2;left:8px;top:16px;transform:rotate(-14deg)}.studio:after{content:'';position:absolute;width:20px;height:28px;background:#c3abc9;left:30px;top:24px;transform:rotate(12deg)}.guide{background:#f4e4c9}.guide:before{content:'';position:absolute;width:26px;height:38px;background:#fff;left:13px;top:9px;transform:rotate(8deg);border:1px solid #c9ad7c}.name strong{font-weight:600;display:block}.name small{color:#505057}.state{font-size:11px;color:#505057}#bulk{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 24px;background:#f3f3f5;border-top:1px solid #ccc}#bulk[hidden]{display:none}.actions{display:flex;gap:6px}#status{margin:18px 0;min-height:18px}footer{padding:0 24px 20px;color:#505057;font-size:11px}@media(min-width:1560px){main{padding:36px}}@media(max-width:600px){main{padding:18px}.bar{padding:12px}.channel{display:none}.title{width:57%}.reach{width:22%}.state{display:block;margin-top:4px}th,td{padding:12px 6px}.art{width:36px;height:42px;flex-basis:36px}.name{gap:6px}#bulk{padding:12px;flex-direction:column;align-items:stretch}.actions{flex-wrap:wrap}header{flex-direction:column;gap:8px}h1{font-size:27px}.bar small{max-width:140px;text-align:right}nav{padding:14px 18px}}</style><nav><strong>Soul / Content workspace</strong><span>Demo collection</span></nav><main><header><div><h1>Content review</h1><p>A final editorial check before your team publishes.</p></div><small>3 fictional drafts · No publishing connection</small></header><section class="surface" aria-label="Content drafts"><div class="bar"><strong>Draft collection</strong><small>Select items to review together</small></div><table><caption>Local sample data. Reach values are illustrative.</caption><thead><tr><th scope="col"><input id="all" type="checkbox" aria-label="Select all drafts"></th><th scope="col" class="title">Content</th><th scope="col" class="channel">Channel</th><th scope="col" class="reach" id="reach-head" aria-sort="none"><button id="sort">Reach ↕</button></th></tr></thead><tbody id="rows"></tbody></table><div id="bulk" hidden><span id="count"></span><div class="actions"><button id="apply">Mark as reviewed</button><button id="cancel">Cancel selection</button></div></div></section><p id="status" role="status">Select a draft to begin. All changes stay in this example.</p></main><footer>Independent Soul interpretation · verified light-mode tokens · system font fallback</footer><script>let items=[{id:'field',title:'A morning in the field',channel:'Instagram',reach:2480},{id:'studio',title:'Inside the studio',channel:'Facebook',reach:1860},{id:'guide',title:'Care guide: summer edition',channel:'Instagram',reach:3210}],selected=new Set(),reviewed=new Set(),direction=0;const rows=document.querySelector('#rows'),all=document.querySelector('#all'),bulk=document.querySelector('#bulk'),status=document.querySelector('#status');function render(){rows.innerHTML=items.map(i=>`<tr><td><input type="checkbox" data-id="${i.id}" aria-label="Select ${i.title}" ${selected.has(i.id)?'checked':''}></td><th scope="row"><div class="name"><div class="art ${i.id}" aria-hidden="true"></div><div><strong>${i.title}</strong><span class="state">${reviewed.has(i.id)?'Reviewed locally':'Not reviewed'}</span></div></div></th><td class="channel">${i.channel}</td><td class="reach">${i.reach.toLocaleString('en-US')}</td></tr>`).join('');rows.querySelectorAll('input').forEach(x=>x.onchange=()=>{x.checked?selected.add(x.dataset.id):selected.delete(x.dataset.id);sync()});sync()}function sync(){all.checked=selected.size===items.length;all.indeterminate=selected.size>0&&selected.size<items.length;bulk.hidden=!selected.size;document.querySelector('#count').textContent=selected.size+' selected'}all.onchange=()=>{selected=all.checked?new Set(items.map(i=>i.id)):new Set();render()};document.querySelector('#cancel').onclick=()=>{selected.clear();render();all.focus()};document.querySelector('#apply').onclick=()=>{let n=selected.size;selected.forEach(x=>reviewed.add(x));selected.clear();render();status.textContent=n+(n===1?' draft':' drafts')+' reviewed locally. Nothing published.';all.focus()};document.querySelector('#sort').onclick=()=>{direction=direction===1?-1:1;items.sort((a,b)=>(a.reach-b.reach)*direction);document.querySelector('#reach-head').setAttribute('aria-sort',direction===1?'ascending':'descending');document.querySelector('#sort').textContent='Reach '+(direction===1?'↑':'↓');render()};render();</script></html>
コードリファレンス
テーブルの選択とソートは HTML に直接実装しています。非公開の Soul DataTable がエクスポートされるとはしていません。React の例では、検証済みの Button エクスポートと、アプリケーション独自のテーブルを使用しています。 実際に使える Soul ボタンを組み込んだ、選択可能なレビューテーブル ドキュメントに記載された Button のエクスポートに、ネイティブのセマンティックテーブルと制御可能なチェックボックスを組み合わせています。テーブルはアプリケーション側のコードであり、Soul の DataTable コンポーネントを独自に作成したものではありません。
tsx コードをコピー
// ContentReview.tsx — inside a configured, authorized Soul application
import React, { useState } from 'react';
import { Button } from '@sbks/ds-button';
import '@sbks/ds-design-library/dist/css/variables.css';
import '@sbks/ds-design-library/dist/css/default-styles.css';
const posts = [
{ id: 'field', title: 'A morning in the field', channel: 'Instagram' },
{ id: 'studio', title: 'Inside the studio', channel: 'Facebook' },
{ id: 'guide', title: 'Care guide: summer edition', channel: 'Instagram' }
];
export default function ContentReview() {
const [selected, setSelected] = useState<string[]>([]);
const [reviewed, setReviewed] = useState<string[]>([]);
const [message, setMessage] = useState('');
const toggle = (id: string) => setSelected(previous =>
previous.includes(id) ? previous.filter(x => x !== id) : [...previous, id]);
const review = () => {
setReviewed(previous => [...new Set([...previous, ...selected])]);
setMessage(`${selected.length} items reviewed locally. Nothing published.`);
setSelected([]);
};
return <section aria-labelledby="review-title">
<h1 id="review-title">Content review</h1>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<caption>Fictional content drafts</caption>
<thead><tr><th scope="col">Select</th><th scope="col">Content</th>
<th scope="col">Channel</th><th scope="col">Review</th></tr></thead>
<tbody>{posts.map(post => <tr key={post.id}>
<td><input type="checkbox" aria-label={`Select ${post.title}`}
checked={selected.includes(post.id)} onChange={() => toggle(post.id)} /></td>
<th scope="row">{post.title}</th><td>{post.channel}</td>
<td>{reviewed.includes(post.id) ? 'Reviewed locally' : 'Not reviewed'}</td>
</tr>)}</tbody>
</table>
{selected.length > 0 && <div aria-label="Bulk actions"
style={{ display: 'flex', gap: 'var(--space-2)', marginTop: 'var(--space-6)' }}>
<Button onClick={review}>Mark {selected.length} as reviewed</Button>
<Button onClick={() => setSelected([])}>Cancel selection</Button>
</div>}
<p role="status">{message}</p>
</section>;
}
コードリファレンス
Button のデフォルトは LIGHT です。この例では、enum のエクスポート先や DataTable の props を推測していません。 まず非公開のデザインライブラリとボタンパッケージをインストールして設定してください。インポート方法は公式 Storybook で検証済みです。 一括レビューで変更されるのはローカルの状態のみです。コンテンツは公開されません。 04
Emplifi Soul デザインシステム:エージェントの設定 実装の出発点 公式クイックスタートの利用には、非公開 npm と Emplifi リポジトリ/VPN へのアクセスが必要です。React 18/19 と Webpack 4/5 に対応し、Sass と SVG ローダーの設定も含まれます。これらのパッケージは公開レジストリでは入手できないため、リンク先の社内スコープ向けオンボーディングを利用してください。単体プレビューには、これらの非公開アセットは必要ありません。
bash / tsx サンプルをコピー
# For authorized Emplifi environments with the private @sbks scope configured:
npm i -D @sbks/ds-design-library @sbks/ds-button
// Application entry:
import "@sbks/ds-design-library/dist/css/variables.css";
import "@sbks/ds-design-library/dist/css/default-styles.css";
// Optional SCSS file:
@use "@sbks/ds-design-library/dist/sass/main.scss" as *; あなたのプロジェクト向けプロンプト AI エージェント用プロンプト プロンプトをコピー
検証済みのレイヤー/カラー/スペーシング変数、13/18 の本文スタイル、6px のコントロールの角丸、8px のコンテナを使って、Emplifi Soul のコンテンツレビュ UI を作成します。安定した ID、部分選択状態に対応した「すべて選択」、1 列のソート機能を備えたセマンティックな選択可能テーブルを使います。認可されたアプリでは、実際の @sbks/ds-button エクスポートと基盤の CSS を使用し、公開 DataTable パッケージを作り出さないでください。 05
Emplifi Soul デザインシステム:参照元と適用範囲 SoulはEmplifiのデザインシステムです。公開されているデザインリファレンスとStorybookは閲覧できますが、公式クイックスタートの利用には非公開のnpmスコープへのアクセスが必要です。ブラウザー上のサンプルは独自のHTML/CSSであり、SoulのReactはバンドルしていません。テーブルの動作はデザインガイダンスを解釈したもので、公開ページではReactは引き続き「開発予定」とされています。