关于 satori
Satori 是将类 JSX 标记和 CSS 转为 SVG 的库,使用自有 Flexbox 布局和文字排版引擎。它面向生成 Open Graph 图片、社交卡片等静态图形的开发者,并仅支持文档列出的部分 CSS。
主要功能
- 01将类 JSX 标记和 CSS 转为 SVG
- 02使用 Flexbox 引擎布局内容
- 03通过提供的字体排版文字
- 04支持静态图片和内联 SVG
- 05提供调试边界框与输出选项
适用场景
- 01生成 Open 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 用于设置画布尺寸。只要渲染了文本,就至少需要一种字体(参见字体)。
限制
这些行为最容易导致输出错误或运行时错误。编写标记前请考虑以下限制。
- 包含多个子元素的每个元素都必须声明
display: 'flex'(或display: 'none')。 Satori 仅支持 Flexbox。包含多个子元素且未显式指定显示方式的div会抛出错误。容器默认应设置display: 'flex'。可以容忍只有一个文本子元素的情况,但显式指定最稳妥。 - 默认的
flexDirection是row,而不是column。 这与人们通常认为 div 会垂直排列的方式相反。需要垂直排列时,请设置flexDirection: 'column'。 padding和margin简写中的每个值都需要显式单位。padding: '0 36'会抛出Expected length unit。请写成padding: '0px 36px';四值形式则写成'0px 36px 36px 36px'。单个不带单位的数值(如padding: 36)没有问题,因为 Satori 会将其视为 px。- 所有内容都使用
flex布局,包括重叠元素。 对于重叠或需要精确定位的元素,请在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、最小/最大宽度和高度、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、两个值)、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(平移、旋转、缩放、倾斜)、transformOrigin、filter、clipPath、遮罩(maskImage、maskPosition、maskSize、maskRepeat)、objectFit、objectPosition、WebkitTextStroke |
| 变量 | --name 声明和 var(--name, fallback) 用法,包括继承和嵌套 |
不支持
| 属性或功能 | 说明和替代方法 |
|---|---|
z-index | 不存在堆叠上下文。元素按文档顺序绘制,因此后面的同级元素会显示在上层。调整标记顺序即可控制层叠关系。 |
calc() | 在值传入样式对象之前,先用 JavaScript 预先计算。 |
color 之外的 currentColor | 仅会为 color 属性解析。边框、背景和填充请设置明确的值。 |
| 3D 变换 | 不支持。仅使用 2D 平移、旋转、缩放和倾斜。 |
min-content、max-content、fit-content | 不支持用于最小/最大宽度和高度。 |
flexBasis: auto | 不支持。请使用明确的基准值,或依赖宽度/高度。 |
| 交互或资源元素 | 不支持 <input>、<style>、<script> 或 <link>。仅渲染静态可见元素。 |
<img> 的 alt、SVG <title> | 会作为可见文本显示在图像上。请省略 alt;从内联 SVG 中移除 <title>(参见“图像和内联 SVG”)。 |
| WOFF2 字体 | 请转换为 TTF、OTF 或 WOFF(参见“字体”)。 |
| AVIF / WebP 图像 | 请转换为 PNG 或 JPEG(参见“图像”)。 |
| 字距调整、连字、OpenType 功能 | 不支持高级排版。 |
| 从右向左书写的语言 | 不支持。 |
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,建议将 base64 数据 URI(或 Buffer/ArrayBuffer)作为 src,以免 Satori 执行额外的网络 I/O。
只有 PNG、JPEG 和 GIF 能可靠解码。AVIF 和 WebP 无法使用,且渲染失败时不会显示任何提示。传入前请将它们转换为 PNG 或 JPEG(例如使用 sharp),并记住,以 .jpg 结尾的现代 URL 仍可能通过内容协商提供 WebP,因此应转换字节数据,而不要仅凭扩展名判断。
不要在 <img> 上添加 alt 属性。 Satori 会将其视为文本内容,因此其值可能泄漏到渲染结果中,作为可见文字绘制在画布上。请省略该属性;输出是静态图片,添加它没有任何好处。
字体
渲染任何文本都至少需要一种字体。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 功能),也不支持从右向左书写的语言。
表情符号
文本字形会使用提供的字体渲染;表情符号不会自动获得。使用 graphemeImages 将特定字素映射到图片源,每张图片会调整为与当前字号相同的正方形:
await satori(<div style={{ display: 'flex' }}>Ship it 🚀</div>, {
...,
graphemeImages: { '🚀': 'https://cdnjs.cloudflare.com/.../1f680.svg' },
})
区域设置
相同的字符在不同区域设置下可能呈现不同效果。可在元素上设置 lang 以强制指定区域设置,例如 <div lang="ja-JP">骨</div>。
动态加载字体和表情符号
当文本片段需要尚未预先提供的字体或表情符号图片时,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 支持矩阵,包含允许的值和默认值,以及全局限制说明。不确定某个属性或值是否受支持时,请查阅此文件。