メタのオープンソースReactデザインシステム「Astryx」にCLIとMCPサーバーを追加し、AIエージェントが読み込めるように
本文の状態
日本語全文を表示中
詳細モードで約11分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
MarkTechPost
メタは8年かけて開発したオープンソースのReactデザインシステム「Astryx」をベータ版として公開しました。このシステムにはCLIとMCPサーバーが搭載され、AIエージェントがUIの構築や文書作成を行えるようになっています。
Continue in AI NEW LAB
このニュースを、実務の判断につなげる
AI NEW LABで、試したことや先に確認したい条件を共有できます。まずはログインなしで読めます。
AI NEW LABで論点を見るSource Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
Meta は今週、Astryx をリリースしました。これは現在ベータ版にあるオープンソースのデザインシステムです。このプロジェクトは Meta のモノレポ内で 8 年間にわたって発展してきました。Astryx は React と StyleX を基盤として構築されています。StyleX は Meta が開発したビルド時の CSS エンジンです。
TL;DR
Astryx は、現在ベータ版にある Meta のオープンソースで AI エージェント対応の React デザインシステムです。
これは、StyleX によるスタイリングと、CSS 変数ベースのテーマカスケード、そして 10 のテーマを組み合わせたものです。
CLI と MCP サーバーにより、AI エージェントが UI の骨組み作成やドキュメント化を可能にします。
Meta 内部では本番環境でテスト済みですが、公開プロジェクトとしてはまだ初期段階です。
Astryx とは何か
Astryx はコンポーネントライブラリと、それを囲むシステムです。基盤となる要素、コンポーネント、テンプレート、テーマを提供します。基盤にはタイポグラフィ、カラー、レイアウト、アクセシビリティが含まれます。公式リポジトリでは 90 以上の React コンポーネントが文書化されています。Meta のドキュメントサイトでは 150 を超えます。コンポーネントには組み込みのスペーシング、ダークモード、柔軟なスタイリング機能が備わっています。テンプレートはダッシュボード、設定画面、フォームなどの完全なページを構成します。ライセンスは MIT です。コードベースの約 4 割 3 分が TypeScript で記述されています。
スタイリング層については理解しておく価値があります。StyleX はビルド時にスタイルを静的なアトミック CSS にコンパイルします。Meta は StyleX を 2023 年末にオープンソース化しました。これは Facebook、Instagram、WhatsApp、Threads の基盤技術となっています。Figma や Snowflake などの外部企業もこれを利用しています。
2 つの設計上の選択が目立ちます。第一に、内部構造はオープンです。すべてのプリミティブがエクスポートされ、組み合わせ可能であり、隠されていません。あらゆるレベルで組み合わせることができます。第二に、間隔(スペース)は自動調整されます。Astryx はこれを文脈認識型間隔補償と呼びます。これにより、手動での修正なしに「二重のパディング」の問題を解消します。
「エージェント対応」の部分
これが他のデザインシステムとの主な違いです。Astryx は、AI エージェントが読み取れるドキュメントとツールを提供しています。コンポーネントには、組み合わせのヒントを含む JSDoc アノテーションが付与されています。CLI(コマンドラインインターフェース)は、開発者が使用するのと同じ API を公開します。また、MCP サーバーも用意されています。MCP は Model Context Protocol(モデル・コンテキスト・プロトコル)のことです。エージェントはこれを使用して、スケルトン作成、ブラウジング、ドキュメント化を行います。
CLI は astryx または略称の xds を通じて呼び出されます。自動化にとって非常に重要な機能の一つがあります。CLI は自己記述型のマニフェストを JSON 形式で返します。これには、すべてのコマンド、引数、フラグ、およびレスポンス型がリストされています。これは CLI 向けの OpenAPI スペックと比較できます。そのため、エージェントは --help テキストをスクレイピングする必要はありません。代わりに、1 つの構造化されたペイロードを読み取るだけです。
Copy CodeCopiedUse a different Browser
npx astryx component Button # コンポーネントの完全なドキュメント
npx astryx template dashboard # 完全なページソースを出力
npx astryx manifest --json # マシン可読なコマンド仕様
テーマと CSS 変数のカスケード
Astryx は、10 の用意されたテーマを同梱しています。それらは default、neutral、daily、butter、chocolate、matcha、stone、gothic、brutalist、y2k と名付けられています。すべてが完全にカスタマイズ可能です。テーマ設定には CSS 変数のカスケード(cascade)が使用されます。変数を変更するだけで、すべてのコンポーネントが自動的に再スタイルされます。コンポーネントのコード自体は変更されません。
以下のインタラクティブなデモでこれを直接確認できます。テーマを選択すると、トークンがリアルタイムで更新される様子をご覧いただけます。
#ax{
--bg:#ffffff;--panel:#f7f8fb;--ink:#161a21;--muted:#5c6573;--line:#e6e9ef;
--accent:#2563eb;--accent-soft:#eef3ff;--brand:#76B900;
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
--sans:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
font-family:var(--sans);color:var(--ink);background:var(--bg);line-height:1.45;
max-width:920px;margin:0 auto;border:1px solid var(--line);border-radius:16px;
overflow:hidden;display:flex;flex-direction:column;box-shadow:0 6px 30px rgba(16,24,40,.06);
}
/* top bar: brand + section tabs */
.deck-top { display: flex; align-items: center; gap: 14px; padding: 11px 16px;
border-bottom: 1px solid var(--line); background:
radial-gradient(700px 120px at 0% -60%, #eef3ff, transparent 70%), var(--bg) }
.brand { font-weight: 800; font-size: 15px; letter-spacing: -.01em; white-space: nowrap }
.brand span { font-weight: 600; font-size: 11px; color: var(--muted) }
.tabs { display: flex; gap: 3px; margin-left: auto; flex-wrap: wrap; justify-content: flex-end }
.tab { font: 600 11.5px/1 var(--mono); color: var(--muted); background: transparent; border: 1px solid transparent;
border-radius: 8px; padding: 6px 8px; cursor: pointer; display: flex; gap: 5px; align-items: center }
.tab:hover { background: var(--panel); color: var(--ink) }
.tab[aria-current=true] { background: var(--accent); color: #fff }
.tab .tl { font-family: var(--sans) }
/* stage holds slides; fixed height = slide */
.stage { position: relative; height: 524px }
.slide { position: absolute; inset: 0; overflow: auto; padding: 20px 24px;
opacity: 0; visibility: hidden; transform: translateY(8px); transition: opacity .26s ease, transform .26s ease }
.slide.is-active { opacity: 1; visibility: visible; transform: none }
.slide::-webkit-scrollbar { width: 8px }.slide::-webkit-scrollbar-thumb { background: #d8dde5; border-radius: 8px }
.knum{font:700 11px/1 var(--mono);color:var(--accent);letter-spacing:.07em}
.slide h2{margin:7px 0 5px;font-size:20px;letter-spacing:-.01em;font-weight:750}
.lead{margin:0 0 12px;color:#2a323d;font-size:13.5px;max-width:74ch}
.lead .m{color:var(--muted)}
.note{font-size:11.5px;color:var(--muted);margin-top:10px;display:flex;gap:6px;align-items:flex-start}
.note b{color:var(--accent);font-weight:700;white-space:nowrap}
/* ボトムコントロール */
.deck-bot{display:flex;align-items:center;gap:12px;padding:10px 16px;border-top:1px solid var(--line);background:var(--panel)}
.arrow{width:30px;height:30px;border-radius:8px;border:1px solid var(--line);background:#fff;color:var(--ink);
font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.arrow:hover{border-color:var(--accent);color:var(--accent)}
.arrow:disabled{opacity:.4;cursor:default;border-color:var(--line);color:var(--muted)}
.counter{font:600 12px/1 var(--sans);color:var(--muted)}
.counter b{color:var(--ink)}
.progress{flex:1;height:4px;background:var(--line);border-radius:999px;overflow:hidden}
.progress i{display:block;height:100%;background:var(--accent);border-radius:999px;transition:width .26s ease}
.mtp{font:700 11px/1 var(--sans);color:var(--brand);white-space:nowrap}
/* facts */
.facts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; margin-top: 6px }
.fact { background: var(--panel); border: 1px solid var(--line); border-radius: 11px; padding: 12px }
.fact b { display: block; font-size: 20px; font-weight: 800; letter-spacing: -.02em }
.fact span { font-size: 11px; color: var(--muted) }
.pills { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px }
.pill { font: 600 11.5px/1 var(--sans); padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px; background: #fff }
.pill.lnk { background: var(--accent); border-color: var(--accent) }
/* cascade widget (scoped vars) */
#cas{--c-bg:#f7f8fa;--c-surface:#fff;--c-text:#1b1f24;--c-muted:#5b6471;--c-border:#e3e7ec;
--c-primary:#2563eb;--c-pt:#fff;--c-accent:#eef3ff;--c-radius:10px;--c-radius-sm:6px;--c-space:12px;
--c-font:var(--sans);--c-weight:600}
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:11px}
.chip{cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--ink);
font:600 11.5px/1 var(--sans);padding:6px 10px;border-radius:999px;display:flex;gap:6px;align-items:center}
.chip[aria-pressed=true]{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)}
.dot{width:10px;height:10px;border-radius:50%}
.casgrid{display:grid;grid-template-columns:1.2fr .85fr;gap:12px}
.prev{background:var(--c-surface);color:var(--c-text);font-family:var(--c-font);border:1px solid var(--c-border);
border-radius:var(--c-radius);padding:13px;display:flex;flex-direction:column;gap:var(--c-space);transition:.2s}
.prev .r{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.b{font:inherit;font-weight:var(--c-weight);font-size:12.5px;cursor:pointer;padding:7px 12px;border-radius:var(--c-radius-sm);border:1px solid transparent}
.b.p{background:var(--c-primary);color:var(--c-pt)}.b.s{background:var(--c-surface);color:var(--c-text);border-color:var(--c-border)}
.bd{font:700 10.5px/1 var(--sans);padding:4px 8px;border-radius:999px;background:var(--c-accent);color:var(--c-primary)}
.in{font:inherit;font-size:12.5px;padding:7px 10px;width:100%;border:1px solid var(--c-border);border-radius:var(--c-radius-sm);background:var(--c-surface);color:var(--c-text)}
.mc{background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--c-radius);padding:11px;display:flex;gap:10px;align-items:center}
.av{width:32px;height:32px;border-radius:50%;background:var(--c-primary);color:var(--c-pt);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;flex:0 0 32px}
.mc .t{font-weight:600;font-size:12.5px}.mc .d{color:var(--c-muted);font-size:11px}
.csw{width:38px;height:22px;border-radius:999px;background:var(--c-border);position:relative;cursor:pointer;flex:0 0 38px}
.csw[aria-checked=true]{background:var(--c-primary)}
.csw::after{content:"";position:absolute;top:3px;left:3px;width:16px;height:16px;border-radius:50%;background:#fff;transition:.2s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.csw[aria-checked=true]::after{transform:translateX(16px)}
.tok{background:#fff;border:1px solid var(--line);border-radius:11px;padding:11px}
.tok h4{margin:0 0 8px;font:700 10px/1 var(--mono);letter-spacing:.06em;color:var(--muted);text-transform:uppercase}
.trow{display:flex;justify-content:space-between;align-items:center;font-size:11px;padding:4px 0;border-bottom:1px dashed var(--line)}
.trow:last-child{border:0}.trow code{font-family:var(--mono);color:var(--muted)}
.tv{font-family:var(--mono);font-size:10.5px;display:flex;align-items:center;gap:5px}
.swatch{width:12px;height:12px;border-radius:4px;border:1px solid var(--line)}
.sld{margin-top:11px}.sld label{display:flex;justify-content:space-between;font:600 11.5px/1 var(--sans);color:var(--muted)}
.sld input{width:100%;margin-top:6px;accent-color:var(--accent)}
/* spacing widget */
.spctrls{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin-bottom:12px}
.tgrow{display:flex;align-items:center;gap:9px;font:600 12.5px/1 var(--sans)}
.tg{width:42px;height:24px;border-radius:999px;background:#cfd5de;position:relative;cursor:pointer}
.tg[aria-checked=true]{background:var(--accent)}
.tg::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#fff;transition:.2s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.tg[aria-checked=true]::after{transform:translateX(18px)}
.spstage{display:grid;grid-template-columns:1fr auto;gap:14px;align-items:center}
.outer{background:#dbe4f7;box-shadow:inset 0 0 0 1px #9db4e6;border-radius:12px}
.inner{background:#bcd0f3;box-shadow:inset 0 0 0 1px #6f93da;border-radius:9px}
.content{background:#fff;border:1px solid var(--line);border-radius:7px;padding:11px;text-align:center;font-size:12px}
.readout{background:#fff;border:1px solid var(--line);border-radius:11px;padding:13px;min-width:170px}
.readout .big{font-size:28px;font-weight:800;letter-spacing:-.02em}
.readout .big.good{color:#1f8f46}.readout .big.bad{color:#c0392b}
.readout .lab{font-size:11px;color:var(--muted)}
.readout .calc{margin-top:7px;font:600 11px/1.5 var(--mono);color:var(--muted)}
/* code + tabs */
.ctabs{display:flex;gap:4px}
.ctab{font:600 11.5px/1 var(--sans);padding:6px 11px;border:1px solid var(--line);border-bottom:0;border-radius:8px 8px 0 0;background:var(--panel);color:var(--muted);cursor:pointer}
.ctab[aria-selected=true]{background:#0f1420;color:#dfe7f5;border-color:#0f1420}
pre.code{margin:0;background:#0f1420;color:#e7edf7;border-radius:0 9px 9px 9px;padding:13px 15px;overflow-x:auto;font:12.5px/1.55 var(--mono)}
.code .k{color:#7cc4ff}.code .s{color:#9ae6a4}.code .c{color:#7a899e}.code .f{color:#f0c674}
.paneprev{border:1px solid var(--line);border-top:0;border-radius:0 0 9px 9px;background:#fff;padding:13px;display:flex;gap:9px;flex-wrap:wrap;align-items:center}
/* terminal */
.term{background:#0f1420;border-radius:11px;overflow:hidden;border:1px solid #1d2738}
.tbar{display:flex;align-items:center;gap:6px;padding:8px 11px;background:#161d2c;border-bottom:1px solid #1d2738}
.tbar i{width:10px;height:10px;border-radius:50%}
.ttitle{margin-left:7px;font:600 11px/1 var(--mono);color:#8a98ad}
.cmds{display:flex;flex-wrap:wrap;gap:6px;padding:10px 11px;background:#121828;border-bottom:1px solid #1d2738}
.cmd{font:600 11.5px/1 var(--mono);color:#cfe0ff;background:#1b2740;border:1px solid #28385a;border-radius:7px;padding:6px 9px;cursor:pointer}
.cmd[aria-pressed=true]{background:#243a5e;border-color:#3b5d91;color:#fff}
pre.out{margin:0;padding:13px 15px;color:#d6e0f0;font:12px/1.5 var(--mono);white-space:pre-wrap;height:188px;overflow:auto}
.out .p{color:#76e0a0}.out .d{color:#7a899e}.out .y{color:#f0c674}.out .b{color:#7cc4ff}
@media(max-width:680px){
.stage{height:600px}
.tab .tl{display:none}
.brand span{display:none}
.facts{grid-template-columns:repeat(2,1fr)}
.casgrid,.spstage{grid-template-columns:1fr}
}
Astryx · インタラクティブ解説
01 · 概要
Astryx とは
Meta が提供するオープンソースの React デザインシステムです。基盤、コンポーネント、テンプレート、テーマが含まれます。React と StyleX(Meta のコンパイル時 CSS エンジン)をベースに構築されており、事前にビルド済みの CSS を提供するため、ビルドプラグインは不要です。現在はベータ版として提供されています。
8 年間の開発期間、Meta 社内にて構築
13,000 以上のアプリを支える
150 以上のコンポーネント(ドキュメントサイト)
10 の事前用意されたテーマ
image github.com/facebook/astryx
MIT ライセンス
CLI + MCP
注記:GitHub リポジトリには「90 以上のコンポーネント」と記載されていますが、Meta のドキュメントサイトでは 150 以上と数えられています。どちらも公式の Astryx ソースに基づくものです。
02 · テーマ設定
CSS 変数のカスケード
テーマは CSS 変数(トークン)のカスケードです。変数を変更すれば、すべてのコンポーネントが再スタイルされますが、コンポーネントのコード自体は一切変更されません。テーマを選択してください:
Primary
Secondary
Badge
A
Astryx Card
トークンがすべてのスタイルを駆動します
有効なトークン
– スケーリングスケール 12px
参考図:このカスケードメカニズムこそが Astryx のテーマ動作の核心であり、各テーマごとのトークン値は代表的な例です。
03 · レイアウト
自動間隔調整 — 「ダブルパディング」の修正
パディング付きのボックスを別のボックスにネストすると、ギャップが積み重なってしまいます。通常は手動でパディングを取り除く必要がありますが、Astryx の文脈認識型間隔補正により、端のギャップを一貫して保つことができます。これを切り替えると — ギャップはレンダリングされた DOM から測定されます。
補正:ON
コンテナパディング 16px
コンテンツ
測定された端のギャップ
16px
IllustrativeThe gap is read from the real DOM. The rule here stands in for Astryx’s internal logic.
04 · ARCHITECTURE
Open internals you can compose and eject
Astryx exports its primitives instead of hiding them, so you compose at any level. When a component is close but not exact, eject its source with the CLI and edit it directly.
Use a component
Eject to customize
import {Button} from '@astryxdesign/core/Button';
import {Badge} from '@astryxdesign/core/Badge';
export default function Toolbar() {
return (
Beta
);
}
Save
Beta
← rendered from the code above
# get the real component source to edit in your repo
$ npx astryx swizzle Button
then import your local copy instead of the package
import {Button} from './components/Button';
Ejected source is yours to change. Astryx stays the default; you own only what you customize.
05 · AGENT READY
A CLI and MCP server agents can read
The CLI exposes the same API a developer uses, plus an MCP server. One call returns a self-describing JSON manifest, so an agent reads structured commands instead of scraping help. Click a command:
astryx · CLI (alias: xds)
RepresentativeCommand の名前とマニフェストの形状は、Astryx CLI の README (v0.0.14) と一致しています。表示のため出力を省略しています。
06 · SETUP
インストールして出荷
最も簡単なパス:Next.js + Tailwind。Astryx は事前構築された CSS を同梱するため、ビルドプラグインは不要です。
# コア、テーマ、および CLI をインストール
npm install @astryxdesign/core @astryxdesign/theme-neutral
npm install -D @astryxdesign/cli
// providers.tsx — アプリを一度ラップする
'use client';
import type {ReactNode} from 'react';
import {Theme} from '@astryxdesign/core/theme';
import {neutralTheme} from '@astryxdesign/theme-neutral/built';
export function Providers({children}: {children: ReactNode}) {
return {children};
}
MITNext · Vite + TailwindCLI + MCPBeta — プロダクション前にテストする
‹
›
01 / 06 · Overview
Marktechpost
// セクションタブを構築
slides.forEach(function(s,i){
var b=document.createElement('button');
b.className='tab';b.type='button';
b.innerHTML=''+String(i+1).padStart(2,'0')+''+s.dataset.title+'';
b.addEventListener('click',function(){go(i);});
tabsEl.appendChild(b);
});
var prevBtn=document.getElementById('prevBtn'), nextBtn=document.getElementById('nextBtn');
prevBtn.addEventListener('click',function(){go(idx-1);});
nextBtn.addEventListener('click',function(){go(idx+1);});
document.addEventListener('keydown',function(e){
if(e.key==='ArrowRight')go(idx+1); else if(e.key==='ArrowLeft')go(idx-1);
});
function go(i){
i=Math.max(0,Math.min(N-1,i));
slides[idx].classList.remove('is-active');
tabsEl.children[idx].setAttribute('aria-current','false');
idx=i;
slides[idx].classList.add('is-active');
slides[idx].scrollTop=0;
tabsEl.children[idx].setAttribute('aria-current','true');
document.getElementById('cnow').textContent=String(i+1).padStart(2,'0');
document.getElementById('ctitle').textContent=slides[i].dataset.title;
document.getElementById('prog').style.width=((i+1)/N*100)+'%';
prevBtn.disabled=(i===0); nextBtn.disabled=(i===N-1);
if(slides[i].dataset.title==='Auto spacing') renderSpacing(); // measure when visible
}
/* ===== 02 cascade ===== */
var THEMES={
default:{dot:'#2563eb',v:{'--c-bg':'#f7f8fa','--c-surface':'#fff','--c-text':'#1b1f24','--c-muted':'#5b6471','--c-border':'#e3e7ec','--c-primary':'#2563eb','--c-pt':'#fff','--c-accent':'#eef3ff','--c-radius':'10px','--c-radius-sm':'6px','--c-weight':'600','--c-font':"'Inter',system-ui,sans-serif"}},
neutral:{dot:'#6b7280',v:{'--c-bg':'#f6f6f5','--c-surface':'#fff','--c-text':'#1f2328','--c-muted':'#6b7280','--c-border':'#e4e4e3','--c-primary':'#3f3f46','--c-pt':'#fff','--c-accent':'#efefee','--c-radius':'8px','--c-radius-sm':'5px','--c-weight':'600','--c-font':"system-ui,sans-serif"}},
daily:{dot:'#0ea5e9',v:{'--c-bg':'#f2f8fc','--c-surface':'#fff','--c-text':'#0f2330','--c-muted':'#5a7180','--c-border':'#d8e8f1','--c-primary':'#0ea5e9','--c-pt':'#fff','--c-accent':'#e2f3fc','--c-radius':'12px','--c-radius-sm':'8px','--c-weight':'600','--c-font':"'Inter',system-ui,sans-serif"}},
butter:{dot:'#d99a00',v:{'--c-bg':'#fffaf0',
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み