satori について
Satori は、JSX風のマークアップと CSS を SVG に変換するライブラリです。独自の Flexbox レイアウトと文字組みエンジンを備え、Open Graph 画像やソーシャルカードなどの静的画像を作る開発者向けです。対応 CSS は記載された範囲に限られます。
主な機能
- 01JSX風マークアップと CSS を SVG に変換
- 02Flexbox エンジンでコンテンツを配置
- 03指定フォントでテキストを組版
- 04静的画像とインライン SVG に対応
- 05デバッグ用境界ボックスと出力設定
活用シーン
- 01Open Graph 画像の生成
- 02ソーシャルカード画像の作成
- 03テキスト主体のデザインを SVG に描画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ usenotra/ skills --skill satoriインストール後、Claude Code で/satoriを入力して読み込まれたか確認します。
同梱ファイルreferences/css-support.md
詳細
Satori
Satori は、JSX に似た HTML と CSS を SVG に変換します。独自の Flexbox レイアウトエンジン(React Native と同じ Yoga エンジン)を実行し、フォントのシェイピングとタイポグラフィを処理したうえで、ブラウザーで描画した場合に近い SVG 文字列を出力します。Open Graph 画像やソーシャルカードを生成するツールの基盤となっており、ラッパーが SVG を PNG に変換して描画します。
Satori のエキスパートとして扱ってください。最も役立つのは、ブラウザー向けの本格的な CSS を記述して予期せず動作しなくなったりエラーになったりするのではなく、最初の描画が正しくなるよう、マークアップをサポート対象の範囲内に収めることです。
基本的な使い方
import satori from 'satori'
const svg = await satori(
<div style={{ color: 'black', display: 'flex' }}>hello, world</div>,
{
width: 600,
height: 400,
fonts: [
{ name: 'Roboto', data: robotoArrayBuffer, weight: 400, style: 'normal' },
],
},
)
satori(...) は SVG 文字列を返します。width と height でキャンバスのサイズを設定します。テキストを描画する場合は、少なくとも 1 つのフォントが必要です(「フォント」を参照)。
制約事項
次の挙動は、出力の誤りや実行時エラーの原因になることが特に多いため、マークアップを記述する前に確認してください。
- 子要素が複数ある要素には、必ず
display: 'flex'(またはdisplay: 'none')を指定してください。 Satori は Flexbox のみをサポートします。子要素が複数あるdivに表示方法を明示しないと、エラーになります。すべてのコンテナーにdisplay: 'flex'を指定するのが基本です。テキストの子要素が 1 つだけの場合は指定しなくても動作しますが、明示するのが最も安全です。 - デフォルトの
flexDirectionはcolumnではなくrowです。 これは、div を縦に積み重ねるという一般的な考え方とは逆です。縦に積み重ねたい場合は、常にflexDirection: 'column'を設定してください。 paddingとmarginの省略記法では、すべての値に単位を明示する必要があります。padding: '0 36'はExpected length unitエラーになります。padding: '0px 36px'と記述し、4 つの値を指定する場合は'0px 36px 36px 36px'としてください。padding: 36のように単独の数値を指定する場合は、Satori が px として扱うため問題ありません。- 重なりを含め、すべてに Flexbox レイアウトを使ってください。 要素を重ねたり、正確な位置に配置したりするには、
position: 'relative'の親要素に対して、position: 'absolute'とtop/left/right/bottomを使います。 - テキストに HTML 文字参照を含めないでください。 Satori はこれらをデコードしないため、
publish‑readyは、画像上でノーブレークハイフンではなく文字列‑としてそのまま表示されます。これは数値参照(‑、 )と名前付き参照( 、&、—)のどちらにも当てはまります。文字列には実際の Unicode 文字を直接記述してください。ノーブレークハイフンにはpublish‑ready(または文字そのもののpublish‑ready)、ノーブレークスペースには、アンパサンドには&、エムダッシュには—を使います。
CSS のサポート
Satori が実装しているのは CSS の一部です。サポート対象の表に記載されていない機能は、サポートされていないものとして扱い、使用する前に確認してください。許可される値とデフォルト値を含む完全な一覧は、references/css-support.md を参照してください。
サポート対象
| カテゴリ | プロパティ |
|---|---|
| レイアウト | display(flex、contents、none)、position(relative、static、absolute)、top/right/bottom/left、width/height、min/max width/height、overflow(visible、hidden) |
| Flex | flexDirection、flexWrap、flexGrow、flexShrink、flexBasis、alignItems、alignContent、alignSelf、justifyContent、gap |
| ボックス | margin、padding、border(幅、solid/dashed のスタイル、色、ショートハンド)、borderRadius、boxSizing、boxShadow、opacity |
| 色と背景 | color、backgroundColor(単一の値)、backgroundImage(linear-gradient、repeating-linear-gradient、radial-gradient、repeating-radial-gradient、url)、backgroundPosition、backgroundSize(cover、contain、auto、2つの値)、backgroundClip(border-box、text)、backgroundRepeat |
| テキスト | fontFamily、fontSize、fontWeight、fontStyle、textAlign、textTransform、textOverflow(clip、ellipsis)、textDecoration、textShadow、lineHeight、letterSpacing、whiteSpace、wordBreak、textWrap(wrap、balance)、textIndent、tabSize、lineClamp |
| 変形と効果 | transform(translate、rotate、scale、skew)、transformOrigin、filter、clipPath、mask(maskImage、maskPosition、maskSize、maskRepeat)、objectFit、objectPosition、WebkitTextStroke |
| 変数 | --name 宣言と var(--name, fallback) の使用(継承とネストを含む) |
サポート対象外
| プロパティまたは機能 | 注記と回避策 |
|---|---|
z-index | スタッキングコンテキストはありません。要素はドキュメント順に描画されるため、後の兄弟要素が前面に表示されます。重なり順を制御するには、マークアップの順序を変更してください。 |
calc() | 値がスタイルオブジェクトに渡される前に、JavaScript で事前計算してください。 |
color 以外での currentColor | color プロパティでのみ解決されます。ボーダー、背景、塗りつぶしには明示的な値を設定してください。 |
| 3D 変形 | サポートされていません。2D の translate、rotate、scale、skew のみを使用してください。 |
min-content、max-content、fit-content | min/max width と height ではサポートされていません。 |
flexBasis: auto | サポートされていません。明示的な basis を指定するか、width/height を使用してください。 |
| インタラクティブ要素またはリソース要素 | <input>、<style>、<script>、<link> は使用できません。静的で表示可能な要素のみが描画されます。 |
<img> の alt、SVG の <title> | 画像上に表示テキストとして描画されます。alt は省略し、インライン SVG から <title> を削除してください(画像とインライン SVGを参照)。 |
| WOFF2 フォント | TTF、OTF、または WOFF に変換してください(フォントを参照)。 |
| AVIF / WebP 画像 | PNG または JPEG に変換してください(画像を参照)。 |
| カーニング、合字、OpenType 機能 | 高度なタイポグラフィはサポートされていません。 |
| RTL 言語 | サポートされていません。 |
HTML 要素
Satori がサポートするのは、静的で表示可能な要素のみです。インタラクティブ要素やリソースを読み込む要素は使用できません。<input>、<style>、<script>、<link> は使えません。Satori は独自の SVG 1.1 ベースのレイアウトエンジンを実行するため、出力がブラウザーとピクセル単位で一致する保証はありません。<div>、<span>、<img>、<svg>、テキストを使用してください。
インライン SVG
インラインの <svg> が機能し、ベクターロゴやアイコンを配置するための信頼できる方法です。マークアップは適切に構成し、viewBox と明示的な width/height を含めてください。グループ化(<g transform=...>)、fill-rule、塗りつぶしのないパスは正しく描画されます。
渡す前に、SVG マークアップから <title> 要素をすべて削除してください。 Satori はこれをテキストコンテンツとして扱うため、タイトルが画像上に表示される文字として描画される場合があります。アイコンライブラリから取得したロゴには <title> が含まれていることが多いため、確認して削除してください。
画像
レイアウトを安定させるため、<img> を使い、width と height を明示的に設定します。
<img src="https://picsum.photos/200/300" width={200} height={300} />
backgroundImage: url(...) を使うと、backgroundSize を設定しない限り、画像は要素に合わせて引き伸ばされます。SVG を後で PNG にラスタライズする場合は、Satori が余分なネットワーク I/O を行わないよう、src には base64 データ URI(または Buffer/ArrayBuffer)を使うことをおすすめします。
確実にデコードできるのは PNG、JPEG、GIF だけです。AVIF と WebP は動作せず、描画に失敗してもエラーは表示されません。これらの画像を渡す前に PNG または JPEG に変換してください(たとえば sharp を使います)。また、.jpg で終わる最新の URL でも、コンテンツネゴシエーションによって WebP が返されることがあるため、拡張子を信用せず、バイト列を変換してください。
<img> に alt 属性を付けないでください。 Satori はこれをテキストコンテンツとして扱うため、その値がキャンバス上に描画され、表示される文字として出力に混入することがあります。属性は省略してください。出力は静的画像なので、alt 属性を付けても利点はありません。
フォント
テキストを描画するには、少なくとも1つのフォントが必要です。Satori は TTF、OTF、WOFF を受け付けます。WOFF2 はサポートされていません。フォントデータは ArrayBuffer(Web)または Buffer(Node.js)として渡してください。
await satori(<div style={{ fontFamily: 'Inter', display: 'flex' }}>Hello</div>, {
width: 600,
height: 400,
fonts: [
{ name: 'Inter', data: inter, weight: 400, style: 'normal' },
{ name: 'Inter', data: interBold, weight: 700, style: 'normal' },
],
})
複数のフォントを渡し、fontFamily を使っていずれかを指定できます。パフォーマンスを向上させるには、フォントを一度定義して、レンダリングごとにオブジェクトを作り直さずに再利用してください。
高度なタイポグラフィ(カーニング、合字、その他の OpenType 機能)はサポートされていません。また、RTL 言語もサポートされていません。
絵文字
テキストのグリフは指定したフォントから描画されますが、絵文字は自動的には使えません。graphemeImages を使って特定の書記素を画像ソースに対応付けます。各画像は、現在のフォントサイズに合わせた正方形にしてください。
await satori(<div style={{ display: 'flex' }}>Ship it 🚀</div>, {
...,
graphemeImages: { '🚀': 'https://cdnjs.cloudflare.com/.../1f680.svg' },
})
ロケール
同じ文字でも、ロケールによって表示が異なることがあります。たとえば <div lang="ja-JP">骨</div> のように、要素に lang を設定してロケールを指定します。
フォントと絵文字の動的読み込み
テキストセグメントに、事前に指定されていないフォントや絵文字画像が必要な場合、Satori は loadAdditionalAsset(code, segment) を呼び出します。code は検出された言語コード、'emoji'、または 'unknown' です。絵文字の場合はデータ URI を、テキストの場合はフォントデータを返してください。
loadAdditionalAsset: async (code, segment) => {
if (code === 'emoji') return `data:image/svg+xml;base64,...`
return loadFontFromSystem(code)
}
出力とレンダリングのオプション
embedFont(デフォルトはtrue):テキストはグリフのアウトラインがインライン化された<path>として出力されるため、後続のツールでフォントファイルは不要です。embedFont: falseにすると、代わりに<text>として出力されます(出力サイズは小さくなりますが、レンダラーにフォントが必要です)。pointScaleFactor:レイアウト値をピクセルグリッドに丸める方法を制御するため、Yoga に渡されます。高 DPI の出力先でより鮮明に表示するには、この値を大きくします。debug: true:境界ボックスを描画します。レイアウトがずれる原因を確認する最も手早い方法です。
ランタイムのサポート
Satori はブラウザー、Node.js(>= 16)、Web Workers で動作します。WASM(Yoga)の依存関係を base64 としてバンドルし、実行時に読み込みます。動的な WASM の読み込みが禁止されている環境では、スタンドアロンビルドを使い、Yoga を自分で初期化してください。
import satori, { init } from 'satori/standalone'
const res = await fetch('https://unpkg.com/satori/yoga.wasm')
await init(await res.arrayBuffer())
const svg = await satori(...)
デバッグのワークフロー
出力が正しくない場合は、大半のケースに当てはまるため、次の項目を順番に確認してください。
- 複数の子要素を持つすべてのコンテナーに
display: 'flex'を設定していますか?displayの設定漏れは最もよくあるエラーで、ずれが起きても何も表示されません。 - 方向は正しいですか? デフォルトは
rowです。縦方向に並べるにはflexDirection: 'column'が必要です。 - ショートハンド値から単位が抜けていませんか?
Expected length unitは、padding/margin/borderの値にpxまたは%が必要であることを示します。 - そのプロパティは実際にサポートされていますか?
references/css-support.mdを確認してください。サポートされていないプロパティは、近似して処理されるのではなく、無視されるかエラーになります。 z-index、calc、またはcolorプロパティ以外でのcurrentColorに依存していませんか? これらはどれも動作しません。マークアップの順序を変更するか、事前に値を計算するか、明示的な値を設定してください。debug: trueを有効にして境界ボックスを確認し、レイアウトツリーが正しいか確かめてください。
リファレンスファイル
references/css-support.md— 許可される値とデフォルト値を含む、サポート対象 CSS の完全な一覧と、全体に適用される制限事項です。プロパティや値がサポートされているかわからない場合は、こちらを確認してください。