当分享网站链接时,许多社交媒体平台可以显示丰富的预览信息(最显著的是封面图片、标题和描述)。Quartz通过合理的默认配置自动处理了大部分设置,但若需要更多控制权,您可以通过配置Frontmatter属性来自定义这些内容。 Quartz还能为每个页面动态生成专属封面图片,用于社交媒体链接预览。要启用此功能,请在quartz.config.ts中设置generateSocialImages: true

效果展示

quartz.config.ts中启用generateSocialImages后,内容创作页面的社交媒体链接预览效果如下:

亮色主题暗色主题

测试时推荐使用opengraph.xyz查看链接在不同平台上的显示效果(更多信息参见本地测试)。

自定义配置

您可以在quartz配置文件中自定义图片生成方式。

例如,当设置generateSocialImages: true时,默认配置如下:

quartz.config.ts
generateSocialImages: {
  colorScheme: "lightMode", // what colors to use for generating image, same as theme colors from config, valid values are "darkMode" and "lightMode"
  width: 1200, // width to generate with (in pixels)
  height: 630, // height to generate with (in pixels)
  excludeRoot: false, // wether to exclude "/" index path to be excluded from auto generated images (false = use auto, true = use default og image)
}

Frontmatter 属性

提示

即使禁用 generateSocialImages,通过 frontmatter 覆盖社交媒体预览属性的功能仍然有效。

以下属性可用于自定义链接预览:

属性别名摘要
socialDescriptiondescription用于预览的描述文字。
socialImageimage, cover预览图片链接。

socialImage 属性应包含相对于 quartz/static 的图片链接。如果您在 quartz/static/my-images 中有图片文件夹,socialImage 的示例可以是 "my-images/cover.png"

信息

封面图片的优先级顺序如下:frontmatter 属性 > 生成图片(如果启用)> 默认图片

默认图片(quartz/static/og-image.png)仅在没有设置其他图片时作为后备使用。如果启用了 generateSocialImages,它将被视为每个页面的新默认图片,但可以通过为页面设置 socialImage frontmatter 属性来覆盖。


完全自定义图片生成

您可以通过将自己的组件传递给 generateSocialImages.imageStructure 来完全自定义生成图片的样式。该组件使用 html/css + 页面元数据/配置选项,并通过 satori 将其转换为图片。Vercel 提供了在线游乐场 用于预览您的 html/css 作为图片的效果。这是原型设计自定义样式的理想方式。

建议在 quartz/util/og.tsx 或其他 .tsx 文件中编写自己的图片组件,否则将其传递到配置中将无法正常工作。默认图片组件示例可在 og.tsxdefaultImage() 中找到。

提示

Satori 仅支持所有有效 CSS 属性的子集。所有支持的属性可在其文档 中查看。

您的自定义图片组件应具有 SocialImageOptions["imageStructure"] 类型,以便于开发。使用此类型的组件时,您将获得以下变量:

imageStructure: (
  cfg: GlobalConfiguration, // global Quartz config (useful for getting theme colors and other info)
  userOpts: UserOpts, // options passed to `generateSocialImage`
  title: string, // title of current page
  description: string, // description of current page
  fonts: SatoriOptions["fonts"], // header + body font
) => JSXInternal.Element

现在,你可以尽情发挥创意,设计属于自己的图片组件了!如需参考或获取灵感,可以查看默认图片组件的标记结构。

示例

以下是一些常用标记的示例,助你快速上手:

  • 获取主题色

    cfg.theme.colors[colorScheme].<colorName>,其中 <colorName> 对应 ColorScheme 中的键名(定义于 quartz/util/theme.ts 文件顶部)

  • 使用页面标题/描述

    <p>{title}</p>/<p>{description}</p>

  • 使用字体族

    详见下文「字体」章节


字体

组件会接收一个包含标题字体和正文字体的数组(首项为标题字体,次项为正文字体)。这些字体对应 quartz.config.tstheme.typography.headertheme.typography.body 的配置,并以 satori 要求的格式传递。在 CSS 中使用时,请通过 .name 属性调用(例如 fontFamily: fonts[1].name 表示使用”正文”字体族)。

使用标题字体的组件示例如下:

socialImage.tsx
export const myImage: SocialImageOptions["imageStructure"] = (...) => {
  return <p style={{ fontFamily: fonts[0].name }}>Cool Header!</p>
}

本地测试

若要在部署前预览页面完整效果,可以通过端口转发进行本地测试。在 VSCode 中,可按照此指南轻松实现(若需使用 opengraph.xyz 等外部工具测试,请确保将 Visibility 设置为 public)。

若启用了 generateSocialImages 功能,可在 public/static/social-images 目录下查看所有生成的图片。

技术信息

生成的图片将保存为 .webp 格式,这种格式能有效控制文件体积(平均图片约 19kB)。同时使用 sharp 进行进一步压缩。

使用图片时,系统会自动设置适当的 Open GraphTwitter 元标签,确保其显示效果符合预期。

示例

除了默认的图片生成模板(位于 quartz/util/og.tsx),您还可以添加自定义模板!您可以选择直接修改源文件(不推荐)或新建文件(例如下方展示的 customSocialImage.tsx 源码)。

添加自定义模板后,可按照以下方式更新 quartz.config.ts 以使用您的图片生成模板:

// Import component at start of file
import { customImage } from "./quartz/util/customSocialImage.tsx"
 
// In main config
const config: QuartzConfig = {
  ...
  generateSocialImages: {
    ...
    imageStructure: customImage, // tells quartz to use your component when generating images
  },
}

以下示例将生成如下所示的图片:

浅色深色

此示例(及默认模板)使用您在 quartz 配置中指定的主题颜色和字体。字体通过 prop 传入,其中 fonts[0] 包含标题字体,fonts[1] 包含正文字体(更多信息请参阅 fonts 章节)。

import { SatoriOptions } from "satori/wasm"
import { GlobalConfiguration } from "../cfg"
import { SocialImageOptions, UserOpts } from "./imageHelper"
import { QuartzPluginData } from "../plugins/vfile"
 
export const customImage: SocialImageOptions["imageStructure"] = (
  cfg: GlobalConfiguration,
  userOpts: UserOpts,
  title: string,
  description: string,
  fonts: SatoriOptions["fonts"],
  fileData: QuartzPluginData,
) => {
  // How many characters are allowed before switching to smaller font
  const fontBreakPoint = 22
  const useSmallerFont = title.length > fontBreakPoint
 
  const { colorScheme } = userOpts
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "row",
        justifyContent: "flex-start",
        alignItems: "center",
        height: "100%",
        width: "100%",
      }}
    >
      <div
        style={{
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          height: "100%",
          width: "100%",
          backgroundColor: cfg.theme.colors[colorScheme].light,
          flexDirection: "column",
          gap: "2.5rem",
          paddingTop: "2rem",
          paddingBottom: "2rem",
        }}
      >
        <p
          style={{
            color: cfg.theme.colors[colorScheme].dark,
            fontSize: useSmallerFont ? 70 : 82,
            marginLeft: "4rem",
            textAlign: "center",
            marginRight: "4rem",
            fontFamily: fonts[0].name,
          }}
        >
          {title}
        </p>
        <p
          style={{
            color: cfg.theme.colors[colorScheme].dark,
            fontSize: 44,
            marginLeft: "8rem",
            marginRight: "8rem",
            lineClamp: 3,
            fontFamily: fonts[1].name,
          }}
        >
          {description}
        </p>
      </div>
      <div
        style={{
          height: "100%",
          width: "2vw",
          position: "absolute",
          backgroundColor: cfg.theme.colors[colorScheme].tertiary,
          opacity: 0.85,
        }}
      />
    </div>
  )
}