メタのオープンソースReactデザインシステム「Astryx」にCLIとMCPサーバーを追加し、AIエージェントが読み込めるように
Meta は、AI エージェントが直接読み取り・操作可能な CLI と MCP サーバーを備えたオープンソース React デザインシステム「Astryx」のベータ版を公開し、UI 構築の自動化を推進した。
キーポイント
エージェント対応設計と MCP サーバー
コンポーネントに JSDoc アノテーションを埋め込み、MCP (Model Context Protocol) サーバーおよび CLI を提供することで、AI エージェントが UI のスキャフォールディングやドキュメント生成を直接実行可能にした。
構造化された CLI と自己記述マニフェスト
CLI コマンドの仕様を JSON 形式で返す「自己記述型マニフェスト」を実装し、エージェントが `--help` テキストをスクレイピングする手間なく、構造化データから正確にコマンドを理解・利用できるようにした。
StyleX による高パフォーマンスなスタイリング
ビルド時に静的アトミック CSS にコンパイルされる Meta の StyleX エンジンを採用し、10 種類のテーマを CSS 変数カスケードで管理することで、コード変更なしに柔軟なデザイン適用を実現している。
内部公開と自動間隔調整機能
プリミティブコンポーネントの内部構造を非公開にせずオープンにしつつ、「文脈対応型間隔補正」により手動での二重パディング修正を不要にするなど、開発者体験と自動化の両立を図っている。
CLI ツールの導入
Astryx にコマンドラインインターフェース (CLI) が追加され、開発者がデザインシステムをより効率的に操作・管理できるようになりました。
MCP サーバーの提供
Model Context Protocol (MCP) サーバーが実装されたことで、AI エージェントが Astryx の設計図やコンポーネントを直接読み込み、理解して活用できるようになりました。
オープンソース化の進展
Meta が公開した React デザインシステムにこれらの新機能を追加し、開発者コミュニティと AI ツール間の連携を強化しています。
影響分析・編集コメントを表示
影響分析
この発表は、デザインシステムが単なる UI コードの集合体から、AI エージェントが自律的に利用・拡張できる「生データ」として進化することを示す重要な転換点です。MCP プロトコルとの統合により、開発現場における UI 構築プロセスの自動化と標準化が加速し、人間による手動コーディングの負担を大幅に軽減する可能性を秘めています。
編集コメント
デザインシステムに AI エージェントの直接操作機能を標準搭載した点は、開発ワークフローの変革において極めて画期的です。特に CLI の仕様を構造化データとして公開するアプローチは、AI ツールとの連携におけるベストプラクティスとなるでしょう。
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 *{box-sizing:border-box}
#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);
}
#ax a{color:var(--accent);text-decoration:none}
#ax a:hover{text-decoration:underline}
/* 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) }
#ax a.pill.lnk { color: #fff }
/* 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
(function(){
var root=document.getElementById('ax');
var slides=[].slice.call(root.querySelectorAll('.slide'));
var tabsEl=document.getElementById('tabs');
var N=slides.length, idx=0;
// セクションタブを構築
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',
原文を表示
Meta released Astryx this week. It is an open-source design system, currently in Beta. The project grew inside Meta’s monorepo over eight years. Astryx is built on React and StyleX. StyleX is Meta’s compile-time CSS engine.
TL;DR
Astryx is Meta’s open-source, agent-ready React design system, now in Beta.
It pairs StyleX styling with a CSS-variable theme cascade and ten themes.
A CLI and MCP server lets AI agents scaffold and document UIs.
It is production-tested inside Meta but young as a public project.
What is Astryx
Astryx is a component library and a system around it. It provides foundations, components, templates, and themes. Foundations cover typography, color, layout, and accessibility. The official repository documents more than 90 React components. Meta’s docs site counts over 150. Components ship with built-in spacing, dark mode, and flexible styling. Templates compose full pages like dashboards, settings, and forms. The license is MIT. TypeScript makes up about three-quarters of the codebase.
The styling layer is worth understanding. StyleX compiles styles to static, atomic CSS at build time. Meta open-sourced StyleX at the end of 2023. It powers Facebook, Instagram, WhatsApp, and Threads. External companies like Figma and Snowflake also use it.
Two design choices stand out. First, internals are open. All primitives are exported and composable, not hidden. You can compose at any level. Second, spacing is automatic. Astryx calls this context-aware spacing compensation. It eliminates ‘double padding’ issues without manual fixups.
The ‘Agent Ready’ Part
This is the main difference from other design systems. Astryx ships docs and tooling that AI agents can read. Components carry JSDoc annotations with composition hints. A CLI exposes the same API a developer uses. There is also an MCP server. MCP is the Model Context Protocol. Agents use it to scaffold, browse, and document.
The CLI is called via astryx or the shorthand xds. One feature is really important for automation. The CLI returns a self-describing manifest as JSON. It lists every command, argument, flag, and response type. Compareing it to an OpenAPI spec for the CLI. So an agent need not scrape --help text. It reads one structured payload instead.
Copy CodeCopiedUse a different Browser
npx astryx component Button # full docs for a component
npx astryx template dashboard # emit full page source
npx astryx manifest --json # machine-readable command spec
Themes and the CSS-Variable Cascade
Astryx ships ten ready-made themes. They are named default, neutral, daily, butter, chocolate, matcha, stone, gothic, brutalist, and y2k. All are fully customizable. Theming uses a CSS variable cascade. You change the variables, and every component restyles. Component code stays untouched.
The interactive demo below shows this directly. Pick a theme and watch the tokens update live.
#ax *{box-sizing:border-box}
#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);
}
#ax a{color:var(--accent);text-decoration:none}
#ax a:hover{text-decoration:underline}
/* 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}
/* bottom controls */
.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)}
#ax a.pill.lnk{color:#fff}
/* 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 · interactive explainer
01 · OVERVIEW
What Astryx is
An open-source React design system from Meta — foundations, components, templates, and themes. Built on React and StyleX, Meta’s compile-time CSS engine. It ships pre-built CSS, so no build plugin is required. Currently in Beta.
8 yrsbuilt inside Meta
13,000+apps it powers
150+components (docs site)
10ready-made themes
image github.com/facebook/astryx
MIT license
CLI + MCP
NoteThe GitHub repo states “over 90 components”; Meta’s docs site counts 150+. Both come from official Astryx sources.
02 · THEMING
The CSS-variable cascade
Themes are a cascade of CSS variables (tokens). Change the variables and every component restyles — component code never changes. Pick a theme:
Primary
Secondary
Badge
A
Astryx Card
tokens drive every style
Active tokens
–spacing scale 12px
IllustrativeThe cascade mechanism is exactly how Astryx themes work; per-theme token values are representative.
03 · LAYOUT
Automatic spacing — the “double padding” fix
Nest a padded box in another and the gaps stack; you normally strip padding by hand. Astryx’s context-aware spacing compensation keeps the edge gap consistent. Toggle it — the gap is measured from the rendered DOM.
Compensation: ON
container padding 16px
Content
Measured edge gap
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 names and the manifest shape match the Astryx CLI README (v0.0.14). Output is shortened for display.
06 · SETUP
Install and ship
Simplest path: Next.js + Tailwind. Astryx ships pre-built CSS, so no build plugin is needed.
install core, a theme, and the CLI
npm install @astryxdesign/core @astryxdesign/theme-neutral
npm install -D @astryxdesign/cli
// providers.tsx — wrap your app once
'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 — test before prod
‹
›
01 / 06 · Overview
Marktechpost
(function(){
var root=document.getElementById('ax');
var slides=[].slice.call(root.querySelectorAll('.slide'));
var tabsEl=document.getElementById('tabs');
var N=slides.length, idx=0;
// build section tabs
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日報で今日の重要ニュースをまとめ読み