mcpField
S
satori

satori

github.com/usenotra设计与 UI7252 次安装7 周前更新
代码仓库

关于 satori

Satori 是将类 JSX 标记和 CSS 转为 SVG 的库,使用自有 Flexbox 布局和文字排版引擎。它面向生成 Open Graph 图片、社交卡片等静态图形的开发者,并仅支持文档列出的部分 CSS。

主要功能

  1. 01将类 JSX 标记和 CSS 转为 SVG
  2. 02使用 Flexbox 引擎布局内容
  3. 03通过提供的字体排版文字
  4. 04支持静态图片和内联 SVG
  5. 05提供调试边界框与输出选项

适用场景

  1. 01生成 Open Graph 图片
  2. 02制作社交卡片图形
  3. 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&#8209;ready 会在图片上原样显示字符 &#8209;,而不是不换行连字符。这同样适用于数字实体(&#8209;、&#160;)和命名实体(&nbsp;、&amp;、&mdash;)。请直接在字符串中写入实际的 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)
FlexflexDirection、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(...)

调试工作流

当输出效果不正确时,请按以下顺序逐项检查,因为这些问题涵盖了绝大多数情况:

  1. 每个包含多个子元素的容器都设置了 display: 'flex' 吗? 未设置 display 是最常见的问题,会导致静默错位。
  2. 方向设置正确吗? 请记住,默认值是 row。垂直排列需要设置 flexDirection: 'column'。
  3. 简写属性值是否遗漏了单位? Expected length unit 表示 padding/margin/border 值需要添加 px 或 %。
  4. 该属性确实受支持吗? 请查看 references/css-support.md。不受支持的属性会被忽略或抛出错误,而不会尝试近似处理。
  5. 是否依赖 z-index、calc,或 color 属性之外的 currentColor? 这些都无法使用;请调整标记顺序、预先计算,或设置明确的值。
  6. 启用 debug: true,查看边界框并确认布局树。

参考文件

  • references/css-support.md — 完整的 CSS 支持矩阵,包含允许的值和默认值,以及全局限制说明。不确定某个属性或值是否受支持时,请查阅此文件。

评论

支持 Markdown