- 前端
- UI组件
- 设计系统
【免费下载链接】theme-ui
Build consistent, themeable React apps based on constraint-based design principles
本篇指南围绕 Theme UI 官方包 @theme-ui/typography 展开,讲解如何把成熟的 Typography.js 主题(如 wordpress-2016、bootstrap、github 等数十款预设)通过toTheme一键转换为 Theme UI 的theme对象,直接注入ThemeUIProvider使用。读完本文,你将掌握该包的安装方式、toTheme的完整用法、可配置参数及其默认值、生成主题对象的内部结构,并能结合源码理解垂直节奏、模块化字阶等底层换算逻辑,最终学会用深合并为转换后的主题补充颜色与全局样式。
一、包定位:在 Theme UI 与 Typography.js 之间架桥
@theme-ui/typography是 Theme UI 官方仓库 packages/typography 目录下的独立包,其职责在 README 中写得很清楚:
Utilities for integrating Typography.js themes with Theme UI
Typography.js 是一套经典的“开箱即用排版”方案,社区积累了数十个预设主题包(wordpress 系列、bootstrap、github、moraga 等)。但这些主题输出的是 Typography.js 自己的样式体系,无法直接被 Theme UI 的theme对象消费。该包通过一个核心函数toTheme完成“格式转换”,让两边生态无缝衔接。
从 src/index.ts 可以看到包的完整导出面,非常精简:
export { toTheme } from './to-theme' export { styles } from './styles' export type { CustomTypographyOptions, ThemeTypographyRhythm as CustomVerticalRhythm, } from './to-theme'即运行时只暴露toTheme与styles两个成员,外加若干类型。这也决定了本文的技术主线:toTheme负责生成主题,styles负责提供配套的排版样式对象。
二、安装与最小可用示例
安装命令(当前仓库 package.json 中的包名为@theme-ui/typography,同时需要theme-ui本体与一个 Typography.js 预设主题):
npm i theme-ui @theme-ui/typography typography-theme-wordpress-2016README 给出的最小示例完整如下,它是整个包的“标准姿势”:
import { ThemeUIProvider } from 'theme-ui' import { toTheme } from '@theme-ui/typography' import wordpress2016 from 'typography-theme-wordpress-2016' const theme = toTheme(wordpress2016) export default (props) => ( <ThemeUIProvider theme={theme}>{props.children}</ThemeUIProvider> )这段代码把typography-theme-wordpress-2016的配置对象直接交给toTheme,得到一个完整的 Theme UI 主题,再通过ThemeUIProvider注入 React 组件树。之后无论是组件内的sx属性、theme上的styles全局样式,还是@theme-ui/components中组件的默认样式,都会基于这份排版主题生效。
官方文档 typography.mdx 还提供了一个可视化佐证:文档站用 typography-layout.js 维护了一个主题切换器,用户下拉选择主题名后执行toTheme(typographyThemes[themeName])即时重渲染,直观对比各排版主题的效果——这印证了toTheme是纯函数式的“配置 → 主题”转换,可安全地动态调用。
三、toTheme 支持的配置参数与默认值
toTheme接收的参数类型是 Typography.js 的TypographyOptions(to-theme.ts 中定义为BaseTypographyOptions)。在源码 to-theme.ts 中,defaults常量给出了完整默认值,配合类型定义可整理为下表:
| 参数 | 默认值 | 含义 |
|---|---|---|
baseFontSize | 16 | 基础字号(单位化处理,见下文),无单位数值 |
baseLineHeight | 1.45 | 正文行高 |
headerLineHeight | 1.1 | 标题行高 |
scaleRatio | 2 | 模块化字阶比例,决定各级标题字号增幅 |
googleFonts | [] | 需要加载的 Google Fonts 列表 |
headerFontFamily | ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'sans-serif'] | 标题字体栈 |
bodyFontFamily | ['georgia', 'serif'] | 正文字体栈 |
headerWeight | 'bold' | 标题字重 |
bodyWeight | 'normal' | 正文字重 |
boldWeight | 'bold' | 加粗字重 |
includeNormalize | true | 是否包含 normalize 重置 |
blockMarginBottom | 1 | 块级元素底部留白(以节奏单位计) |
其中headerFontFamily与bodyFontFamily会经过 getFonts 处理:每个字体名通过 wrapFontFamily 判断,若属于genericFontFamilies(serif、sans-serif、monospace、-apple-system、system-ui等)则不加引号,否则加单引号包裹,最终拼接成 CSS 字体栈字符串。
四、转换后主题对象的内部结构
调用toTheme返回的对象(快照见 to-theme.ts.snap),是一个完全 Theme UI 风格的主题,包含以下字段:
4.1 六大主题刻度
以默认配置为例,从快照可以看到每个刻度都是 Theme UI 可直接消费的格式:
space:[0, 5.75, 11.5, 23, 46, 92, 184]。由 getSpace 生成,先取rhythm(opts.blockMarginBottom)得到一个基础节奏单位,再乘以[0, 1/4, 1/2, 1, 2, 4, 8]得到 7 级间距。fonts:body为'georgia', serif,heading为完整的系统字体栈。fontSizes:[12.996, 13.929, 16, 21.112, 24.251, 32]。由 getFontSizes 基于模块化比例换算得出:对[-1.5/5, -1/5, 0, 2/5, 3/5, 1]六个指数调用getScale,即modularscale(value, scaleRatio) * baseFontSize(依赖modularscale包)。fontWeights:{ body: 'normal', bold: 'bold', heading: 'bold' }。lineHeights:{ body: 1.45, heading: 1.1 },直接取自baseLineHeight与headerLineHeight。typography:保留 Typography.js 的rhythm函数与规范化后的options,类型为ThemeTypographyRhythm(to-theme.ts),供需要直接调用节奏计算的场景使用。
4.2 styles:面向 Markdown 内容的全局排版样式
theme.styles来自独立的 styles.ts,该文件头注释明确了它与 Typography.js 原生样式输出的三点差异:
- 只包含 Markdown 元素(
h1~h6、p、ul、ol、table、blockquote、hr、pre、img等)的样式; - 不包含颜色样式;
- 不包含响应式样式。
其核心设计是通过主题引用而非硬编码数值:标题统一继承fontFamily: 'heading'、lineHeight: 'heading'、fontWeight: 'heading'(styles.ts),root则引用fontFamily: 'body'、fontSize: 2、fontWeight: 'body'、lineHeight: 'body',全部指向第一节生成的主题刻度。此外每个块级元素统一获得padding: 0, margin: 0, marginBottom: 3(通过blockElements.reduce批量注入),保证排版节奏一致。测试快照 styles.ts 测试 完整记录了这套结构的展开结果。
五、转换原理:toTheme 在内部做了什么
toTheme的实现位于 to-theme.ts,源码注释概括了设计意图:
- 使用无单位数值
- 创建基础 theme 对象
- 使用静态的
theme.styles对象供 Theme UI 消费- 忽略
overrideThemeStyles- 不包含颜色样式
- 与现有 Typography.js 主题基本兼容
其处理管线分为三步:
- 过滤不需要的选项:
pruneOptionsFromUnwanted(to-theme.ts)从传入配置中剔除headerColor、bodyColor、overrideStyles、overrideThemeStyles、plugins五类选项——它们依赖 DOM/主题钩子或已超出 Theme UI 主题的职责范围。 - 强制无单位化:
toUnitlessOptions(to-theme.ts)通过parseFloat把baseFontSize之类可能带单位的字符串(如'16px')转成纯数字。 - 计算垂直节奏:将合并后的完整配置交给
compass-vertical-rhythm(package.json 中可见依赖),并以rhythmUnit: 'px'、baseFontSize字符串化后的值构造节奏对象;随后分别计算space、fonts、fontSizes、fontWeights、lineHeights,最后挂上静态styles与typography元信息。
六、扩展与覆写:给排版主题补上颜色
Typography.js 主题天然不含颜色(源码明确“不包含颜色样式”),因此实际落地时几乎总是需要再合并颜色与全局链接样式。官方文档 typography.mdx 给出用deepmerge扩展的写法:
import { toTheme } from '@theme-ui/typography' import wp2016 from 'typography-theme-wordpress-2016' import merge from 'deepmerge' const theme = merge(toTheme(wp2016), { colors: { text: '#000', background: '#fff', primary: '#07c', }, })仓库内的 typography 示例项目 展示了更完整的实战形态(theme.tsx):先toTheme(fairyGates)得到排版主题,再用lodash.merge合并colors,并覆写styles.root(设置color: 'text'、bg: 'background')与styles.a(链接用primary/secondary着色并处理 hover 态)。该示例的页面入口在 index.mdx,包装层使用 index.tsx 中的WrapRootElement注入ThemeUIProvider——这正是 Gatsby 场景下的推荐接入方式。
合并时注意两点:深合并(浅展开会丢失嵌套的styles结构);排版主题的styles.root只定义了字体与行高,需自行补充color/bg,否则页面可能没有背景色(示例中即显式补了这两个属性)。
七、兼容性与验证:数十款 Typography.js 主题实测
包的兼容性有充分的测试与真实使用依据:
- 测试夹具 fixtures/themes.ts 一次性导入了 40 余款
typography-theme-*预设(alton、bootstrap、github、irving、moraga、wordpress-2010~2016、kubrick、trajan 等),测试 to-theme.ts 对每款主题执行toTheme并生成快照(snapshot %s用例),同时校验fontSizes[0]与 Typography.js 原生输出的h6字号一致、theme.typography.rhythm与原生typo.rhythm计算结果完全相等。 - 文档站 typography-themes.js 同样罗列了 30+ 款可切换预览的主题,说明这些预设均可直接接入。
- 包内还包含 declarations.d.ts,为
typography-theme-*通配模块补充类型声明,方便 TypeScript 使用者。
此外,文档站的 typography.mdx 展示了“选中主题 →toTheme→ 重渲染”的演示闭环,是理解该包实际运行效果的最直观材料。
八、使用限制与注意事项
综合源码注释与实现,使用该包时有几点边界需要明确:
- 忽略
overrideThemeStyles/overrideStyles/plugins:这些 Typography.js 特性会被 pruneOptionsFromUnwanted 静默移除,若预设主题大量依赖它们,个别细节可能不会迁移过来。 - 不含颜色与响应式样式:生成的
styles只覆盖 Markdown 元素的版式部分,颜色、媒体查询需要自行通过深合并补充(见第六节)。 googleFonts只写入typography.options:从 getFonts 的实现看,字体栈本身不含网络字体的<link>加载逻辑;文档站示例中是通过 typography-layout.js 额外挂载GoogleFonts组件来实际加载字体,自建应用时需自行处理。- 节奏换算基于 px 单位:
rhythmUnit: 'px'是硬编码的(to-theme.ts),space/fontSizes输出的是无单位数值,适合 Theme UI 的主题刻度体系,但如果项目要求 rem 布局,需要在此基础上自行换算。
九、总结:接入路径一览
要在自己的 Theme UI 项目中复用 Typography.js 的排版设计,完整路径是:
- 安装
theme-ui、@theme-ui/typography与所选typography-theme-*预设; - 用
toTheme(preset)生成基础主题对象; - 用
deepmerge或lodash.merge合并colors、补充styles.root的color/bg及链接样式; - 将合并结果传给
ThemeUIProvider,在sx属性与组件中即可直接引用space、fonts、fontSizes、lineHeights、fontWeights刻度; - 若使用 Markdown/MDX 内容,配合
theme.styles即可获得一致的标题、列表、表格、引用块排版。
相关源码与文档索引:包源码、styles 定义、单元测试、文档页、示例项目。
- 前端
- UI组件
- 设计系统
【免费下载链接】theme-ui
Build consistent, themeable React apps based on constraint-based design principles
相关推荐
Docker 容器重启策略(Restart Policies)实战指南:从 `always` 到 `unless-stopped` 的逐层拆解
Docker 容器重启策略(Restart Policies)实战指南:从 always 到 unless stopped 的逐层拆解 导读 本篇基于 devo
前端UI组件设计系统naive-ui 试验性特性指南:使用 @naive-ui/tusimple-theme 集成图森(Tusimple)主题
naive ui 试验性特性指南:使用 @naive ui/tusimple theme 集成图森(Tusimple)主题 本文以 naive ui 官方文档中
前端UI组件Material Theme UI主题转换工具:将VSCode主题转换为JetBrains格式
Material Theme UI主题转换工具:将VSCode主题转换为JetBrains格式 作为开发者,你是否曾在JetBrains系列IDE(如Intel
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考