首页
/ Storybook Backgrounds 背景配置完全指南:在 .storybook/preview 中自定义颜色选项与初始背景

Storybook Backgrounds 背景配置完全指南:在 .storybook/preview 中自定义颜色选项与初始背景

2026-09-06 19:01:00作者:郦嵘贵Just

导读

Backgrounds(背景色)是 Storybook 内置的核心功能,用于控制 Story 在 UI 中渲染时所处的背景颜色,帮助你在不同明暗背景下校验组件表现。本文将围绕当前仓库中 docs/_snippets/addon-backgrounds-options-in-preview.md 展开,讲解如何通过 .storybook/preview.* 中的 backgrounds.options 参数自定义可用的背景色列表,并通过 initialGlobals 设定初始背景色;同时结合 code/core/src/backgrounds 下的真实源码,剖析这些配置从参数解析到最终注入样式的底层链路。读完本文,你将能够配置全局背景色板、理解 options 键值与 globals 值之间的对应关系,并为任意组件或单个 Story 做局部覆写。

Backgrounds 功能与默认配置

Backgrounds 功能负责决定每个 Story 渲染时所处的画布背景。在 Storybook 中它是 Essentials 插件之一,相关完整文档见 docs/essentials/backgrounds.mdx

开箱即用时,该功能内置了明暗两种背景。当前仓库中该功能的实现代码位于 code/core/src/backgrounds,其默认值定义在 code/core/src/backgrounds/defaults.ts

export const DEFAULT_BACKGROUNDS: BackgroundMap = {
  light: { name: 'light', value: '#F8F8F8' },
  dark: { name: 'dark', value: '#333' },
};

也就是说,工具栏背景色下拉中默认展示两个选项:键 light(浅色 #F8F8F8)与键 dark(深色 #333)。文档示例片段中展示的 dark: '#333' 与源码默认值一致,light 的具体色值以你安装版本实际生效值为准。

你并不局限于这组默认色,可以通过 .storybook/preview.* 中的 parameters.backgrounds.options 完全自定义自己的色板,并用 initialGlobals 指定 Storybook 启动后 Story 默认采用的背景色。下面逐步展开。

在 .storybook/preview 中配置全局背景选项

配置入口

背景配置应放在 .storybook/preview.js|jsx|ts|tsxparameters 中。由于 parametersinitialGlobals 遵循 Storybook 的层级合并规则,放在 preview.* 中的配置会对项目中所有组件与所有 Story 全局生效。

CSF 3 写法(JavaScript / 通用)

export default {
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
};

CSF 3 写法(TypeScript / 通用)

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';

const preview: Preview = {
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
};

export default preview;

options 对象结构说明

backgrounds.options 的类型定义可以在 code/core/src/backgrounds/types.ts 中找到:

export interface Background {
  name: string;
  value: string;
}

export type BackgroundMap = Record<string, Background>;
  • 对象键(如 darklightmaroon:背景的标识符,用于在 toolbar 中定位、也用于 globals.backgrounds.value 的取值匹配。必须保持唯一且字符串形式;
  • name:显示在工具栏下拉菜单中的文案(如 DarkLightMaroon);
  • value:实际应用在画布上的 CSS 颜色值,支持任意合法的 CSS 颜色表达(十六进制、rgb()rgba()、命名颜色等)。

示例中先“重写”了默认的两个选项,再新增自定义色 maroon。这种写法意味着:preview 级别的 options 会覆盖整个默认色板。若你只需要在默认明暗基础上追加颜色,请在对象中同时保留 dark/light 键(如示例所示),否则它们会消失。

用 initialGlobals 设置初始背景

initialGlobals 负责设置 Storybook 启动时的初始全局状态。对 Backgrounds 而言,其全局状态以 backgrounds 为命名空间(见 code/core/src/backgrounds/preview.ts):

const initialGlobals: Record<string, GlobalState> = {
  [PARAM_KEY]: { value: undefined, grid: false },
};

value 必须与 options 中的某个键匹配。例如上面的配置将 value 设为 'light',于是首屏 Story 会直接以浅色背景 #F7F9F2 渲染,无需手动切换。这一设计也支持仅通过 initialGlobals 换初始色、而不改动 options 色板的常见需求。

CSF Next 时代的写法(definePreview)

在 CSF Next 实验语法中,.storybook/preview 使用 definePreview() 包装配置,parametersinitialGlobals 的写法保持不变。以下展示当前仓库文档中给出的各渲染器变体。

React

// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { definePreview } from '@storybook/your-framework';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { definePreview } from '@storybook/your-framework';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});

Vue 3

import { definePreview } from '@storybook/vue3-vite';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});
import { definePreview } from '@storybook/vue3-vite';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});

Angular

import { definePreview } from '@storybook/angular';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});

Web Components

import { definePreview } from '@storybook/web-components-vite';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});
import { definePreview } from '@storybook/web-components-vite';

export default definePreview({
  parameters: {
    backgrounds: {
      options: {
        // 👇 Default options
        dark: { name: 'Dark', value: '#333' },
        light: { name: 'Light', value: '#F7F9F2' },
        // 👇 Add your own
        maroon: { name: 'Maroon', value: '#400' },
      },
    },
  },
  initialGlobals: {
    // 👇 Set the initial background color
    backgrounds: { value: 'light' },
  },
});

注意:不同渲染器对应不同的包名,示例中的 @storybook/your-framework 需要替换为实际使用的包(如 @storybook/react-vite@storybook/nextjs@storybook/vue3-vite@storybook/angular@storybook/web-components-vite 等),具体以 code/addons 及各框架渲染器实际导出的 Preview 类型为准。

底层原理:从 options 到画布背景样式

.storybook/preview 里声明的这段配置,最终由 Backgrounds 的预览插件消费。其入口在 code/core/src/backgrounds/preview.ts,通过 definePreviewAddon 把装饰器 withBackgroundAndGrid、默认参数与初始全局状态注册进 Storybook。其中默认参数(code/core/src/backgrounds/preview.ts)为 disable: false,grid 默认 cellSize: 20opacity: 0.5cellAmount: 5

真正把配置应用出去的是装饰器 code/core/src/backgrounds/decorator.ts,关键处理逻辑可以概括为:

  1. 读取配置:从 parameters[backgrounds] 解构出 options(缺省时回退为 DEFAULT_BACKGROUNDS)、disablegrid
  2. 解析全局值:从 globals[backgrounds] 取出 value(兼容字符串或 { value } 两种形态,见 types.ts),即当前选中的背景键;
  3. 查表取值:用该键在 options 中查找对应条目,取 value 字段作为 CSS 颜色;若找不到匹配,则退化为 'transparent'
  4. 注入样式:通过 addBackgroundStyle.sb-show-main(story 模式)或 docs 模式下的 #anchor--… .docs-story 容器注入 background: <color> !important; 样式(decorator.ts);
  5. 禁用判断shownBackground = !!item && !disable,只有当选中项存在且未禁用该功能时,才会真的渲染背景色。

这解释了为什么 initialGlobals.backgrounds.value 必须是 options 的某个键——它本质上是查表的索引。同时也可以看到,当配置的 options 为空对象或未提供时,代码会自动使用默认的明暗两项作为兜底。

此外,装饰器会遵循系统 prefers-reduced-motion 设置,仅在允许动画时注入 transition: background-color 0.3sdecorator.ts),避免无障碍场景下不必要的色彩过渡。

局部覆写:组件级与 Story 级 options

除了在 preview.* 全局配置,backgrounds 参数遵循 Storybook 的参数继承机制,可在组件 Meta 层单个 Story 层进行局部覆写。

例如,仅针对某个组件的全部 Story 调整色板,可在其 Button.stories.ts 中配置 parameters.backgrounds.options,完整的多渲染器示例见 docs/_snippets/addon-backgrounds-options-in-meta.md,核心思路如下:

const meta = {
  component: Button,
  parameters: {
    backgrounds: {
      options: {
        // 👇 Override the default `dark` option
        dark: { name: 'Dark', value: '#000' },
        // 👇 Add a new option
        gray: { name: 'Gray', value: '#CCC' },
      },
    },
  },
} satisfies Meta<typeof Button>;

export default meta;

在该例子中,dark 选项在组件层级被改写为 #000,并新增 gray 选项。结合 Storybook 的合并规则,这会对该组件下的所有 Story 生效。

为指定 Story 固定背景(globals)

如果想让某个 Story 永远渲染在指定背景上(例如需要展示“深色模式下”的效果),可以通过 globals 直接绑定颜色,示例参见 docs/_snippets/addon-backgrounds-define-globals.md

export default {
  component: Button,
  globals: {
    // 👇 Set background value for all component stories
    backgrounds: { value: 'gray', grid: false },
  },
};

export const OnDark = {
  globals: {
    // 👇 Override background value for this story
    backgrounds: { value: 'dark' },
  },
};

需要特别注意:一旦通过 globals 为某个 Story 指定了 backgrounds.value,该颜色会被强制应用,无法再用工具栏切换。这正是 docs/essentials/backgrounds.mdx 中 Callout 提示强调的行为,适合用来保证回归测试或文档页中 Story 始终处于确定底色。若只是想设置“初始显示”的背景、同时保留用户在工具栏切换的自由度,应使用 initialGlobals(全局启动态)而不是 globals

周边配置速查:disable、grid 与完整 API

理解 preview 配置后,Backgrounds 命名空间下还有几个同源参数常与 options 搭配使用,详见 docs/essentials/backgrounds.mdx 的 API 段落。

disable

类型 boolean。置为 true 可关闭背景功能,典型用法是在某个 Story 上单独关闭,例如 docs/_snippets/addon-backgrounds-disabled.md 中的:

export const Large = {
  parameters: {
    backgrounds: { disable: true },
  },
};

若需要在整个 Storybook 级别关闭该功能,建议在 main.* 的 addons 配置中处理而非逐文件配置 disable。在 code/core/src/backgrounds/decorator.ts 可以看到,disable 直接参与 shownBackground 计算,最终决定样式是否注入。

grid

背景网格用于快速核对组件对齐情况。网格没有额外配置也能工作,如需自定义可在 parameters.backgrounds.grid 中提供,可用属性包括:

属性 类型 说明
cellAmount number 次要网格线数量,默认 5
cellSize number 主要网格线尺寸,默认 20
disable boolean 关闭网格
offsetX / offsetY number 网格偏移,默认在 fullscreen 布局下为 0padded 布局下为 16(docs 模式下为 20
opacity number 网格线透明度,默认 0.5

完整可运行的网格配置示例见 docs/_snippets/addon-backgrounds-grid.md。该参数与 options 一样支持在 preview、meta、story 各级配置。需要注意区分默认值:preview.ts 中插件声明层给出的网格默认值为 cellSize: 20 / opacity: 0.5 / cellAmount: 5preview.ts),而装饰器内部用于兜底的 defaultGridcellSize: 100 / cellAmount: 10 / opacity: 0.8decorator.ts),前者通常先于后者生效。

globals 与 parameters 完整类型

backgrounds 全局状态支持两种形态:{ value?: string; grid?: boolean } 或直接的 value 字符串;参数对象结构在 types.ts 中有完整 TypeScript 定义,含 defaultdisablegridoptions 四个可选字段,其中 default 用于声明默认背景键,options 即本文核心配置项。对比可以发现 docs 与源码在个别历史字段(如 default)上可能存在版本差异,以你所安装 Storybook 版本的导出类型为准。

如何验证配置是否生效

配置完成后,可借助仓库中已有的验证资源确认行为:

  • Story 层面:参考模板 Story code/core/template/stories/backgrounds/globals.stories.ts,它演示了通过 globals 声明背景值的标准用法;
  • 端到端测试:查看 code/e2e-sandbox/addon-backgrounds.spec.ts,了解 Playwright 如何断言背景色切换、网格展示与 docs 模式下背景注入等行为;
  • 运行 Storybook 后在工具栏中展开背景下拉,确认自定义色名称(name)与色值列表出现;切换选项后,检查画布容器 background 的计算样式是否等于对应 value

小结

.storybook/preview.* 中配置 Backgrounds 是让整个组件库统一背景规范的最直接手段:parameters.backgrounds.options 决定“有哪些背景可用”,initialGlobals.backgrounds.value 决定“启动时用哪一个”。若需要精确到组件或 Story 粒度,则可配合 parameters(改色板)与 globals(锁定背景)按层级覆写。结合 code/core/src/backgrounds/decorator.ts 的源码可以看出,这一切配置最终都收敛为一次“按 options 键查表 → 将 CSS 颜色注入预览容器”的简单而可预测的行为,理解这条链路之后,你就能从容定制出贴合团队设计规范的背景工作流。

登录后查看全文
热门项目推荐
相关项目推荐