首页
/ Material UI System 排版工具指南:借助 sx 与 theme.typography 精细控制文本样式

Material UI System 排版工具指南:借助 sx 与 theme.typography 精细控制文本样式

2026-09-06 18:32:05作者:舒璇辛Bertina

本文基于 MUI 仓库 docs/data/system/typography/typography.md 文档,围绕 @mui/system 提供的 Typography 排版工具函数展开。这套工具把 CSS 排版属性映射为可在 sx 中直接书写的系统 Prop(typographyfontFamilyfontSizefontWeighttextAligntextTransformfontStyleletterSpacinglineHeight),并自动与 theme.typography 主题对象联动。读完本文,你将掌握在 Material UI 组件中快速实现排版变体、对齐、截断与字重控制的具体写法,并理解其底层解析机制,能在自己的设计中复用这套"主题驱动"的排版方案。

一、Typography 系统工具能解决什么问题

在 Material UI 中,除了直接使用 <Typography> 组件表达语义化标题/正文外,很多时候我们只是需要给某个容器内的文字临时套用一种排版样式:这一行居中、那一段用正文小字、某个标签加粗。如果每次都为此引入独立的样式类或重复编写长串 CSS,既繁琐又难以与主题保持统一。

Typography 系统工具正是为此设计的。它是一组 style function(样式函数),把所有常见的文字排版能力(对齐、换行、字重、字号、字体族、行高、字间距、字母转换)压缩成一行 sx 代码:

<Box sx={{ typography: 'subtitle2' }}>…</Box>      // 直接套用 theme.typography.subtitle2
<Box sx={{ textAlign: 'center' }}>…</Box>
<Box sx={{ fontWeight: 'bold' }}></Box>

它的核心优势有两个:

  • 写起来简洁:无需新增组件或 class,sx 内部会把上述 Prop 编译成真正的 CSS 规则;
  • 与主题强绑定fontWeight: 'light' 这类语义值不是硬编码的魔法数字,而是被解析为 theme.typography.fontWeightLight 对应的主题 token,改主题即可全局联动。

这套能力来源于 @mui/system 包。Material UI(@mui/material)的所有组件都支持 sx,因此下面的写法在 Button、Card、TableCell 等任何组件的 sx 中同样适用,而不只限于 Box。在文档仓库中,本节对应的可运行 Demo 均存放在 docs/data/system/typography 目录下(每个示例都同时提供 .js.tsx 两个版本以及 .tsx.preview 预览文件)。

二、核心机制:style function 如何把 Prop 变成 CSS

在深入各类写法之前,先了解源码层的执行原理,能帮助你理解"为什么某些取值需要从主题中解析、某些取值会直接透传"。

所有排版工具函数都定义在 typography.ts 中。它们由 style() 工厂函数生成,例如:

// packages/mui-system/src/typography/typography.ts(节选)
export const fontFamily = style({ prop: 'fontFamily', themeKey: 'typography' });
export const fontSize = style({ prop: 'fontSize', themeKey: 'typography' });
export const fontWeight = style({ prop: 'fontWeight', themeKey: 'typography' });

export const typographyVariant = style({
  prop: 'typography',
  cssProperty: false, // 不映射到单一 CSS 属性,而是整体展开
  themeKey: 'typography',
});

const typography = compose(
  typographyVariant,
  fontFamily,
  fontSize,
  fontStyle,
  fontWeight,
  letterSpacing,
  lineHeight,
  textAlign,
  textTransform,
);

从源码可以看出几个要点:

  1. fontFamilyfontSizefontStylefontWeight 都声明了 themeKey: 'typography',而 letterSpacinglineHeighttextAligntextTransform 没有主题键,因此后者取值会直接作为 CSS 值透传。
  2. typographyVariantcssProperty: false 意味着它不生成 { [prop]: value } 这样的单属性对象,而是把主题里某个排版变体(如 theme.typography.body1整组展开font-familyfont-weightfont-sizeline-heightletter-spacingtext-transform 等完整样式。
  3. 九个函数最终通过 compose 合并成一个聚合 style function,可整体使用。

取值解析逻辑位于 style.ts 中:字符串取值会被当作主题对象里的点路径(dot path)去查询(例如 'h6.fontSize' 会被 pathInput.split('.') 拆成 ['h6', 'fontSize'] 逐级访问);若主题中找不到对应键,则回退使用你传入的原始值。因此当你传入的主题键存在时走主题、不存在时按原始 CSS 值透传。同时,每次取值都会经过 handleBreakpoints(见 style.ts),这正是 sx 中排版 Prop 也能写 { xs: ..., md: ... } 响应式对象的原因。

三、Variant:一键套用主题排版层级

MUI 默认主题在 theme.typography 下内置了完整的排版层级。你可以直接把某个变体名作为 typography 的值,一次性获得该层级定义的整套字体属性:

<Box sx={{ typography: 'subtitle2' }}>subtitle2</Box> // 套用 theme.typography.subtitle2
<Box sx={{ typography: 'body1' }}>body1</Box>         // 套用 theme.typography.body1
<Box sx={{ typography: 'body2' }}>body2</Box>         // 套用 theme.typography.body2

更完整的变体示例见 Variant.js

可用变体名包括默认主题中的 h1h6subtitle1subtitle2body1body2buttoncaptionoverline 等。由于 typography Prop 采用点路径 + 主题查询的机制,前提是该键确实存在于 theme.typography;当你为组件定制了额外的变体键(或在主题中删改了层级),这里会随之生效。若写入一个主题中不存在的字符串,则会原样透传并产生无效 CSS,因此建议始终以主题变体键为准。

typography 变体与 Material UI 的 <Typography variant="..."> 使用的是同一份主题数据。区别在于:<Typography> 是一个承载语义(如 component="h1")与自动映射的独立组件;而这里的 typography Prop 只是纯样式快捷方式,适合套用在任意容器文字上,语义结构由你自己控制。

四、Text alignment:文本对齐

控制文本对齐只需使用 textAlign Prop,取值与 CSS text-align 完全一致:leftcenterrightjustifystartend 等。

<Box sx={{ textAlign: 'left' }}>…</Box>
<Box sx={{ textAlign: 'center' }}></Box>
<Box sx={{ textAlign: 'right' }}></Box>

参考 TextAlignment.js 中可以看到一个完整的 justify 段落示例(两端对齐)与左/中/右三种对齐组合的演示。由于 textAligntypography.ts 中没有声明 themeKey,所有取值都直接透传为 CSS,不受主题影响。

五、Text transformation:大小写转换

textTransform 让你在不改动源文本内容的前提下改变大小写呈现:

<Box sx={{ textTransform: 'capitalize' }}>…</Box> // 每个单词首字母大写
<Box sx={{ textTransform: 'lowercase' }}>…</Box>  // 全部小写
<Box sx={{ textTransform: 'uppercase' }}>…</Box>  // 全部大写

完整示例见 TextTransform.js。这在实际项目中非常适合用来统一"按钮文字全大写"这类设计规范,而无需逐处修改文案。它同样不带主题键,值会原样写入 CSS text-transform

六、Font weight:字重与主题字重 token

字重有两种写法:语义 token(映射到 theme.typography)与直接数值。

<Box sx={{ fontWeight: 'light' }}>…</Box>   // 解析为 theme.typography.fontWeightLight
<Box sx={{ fontWeight: 'regular' }}>…</Box> // 解析为 theme.typography.fontWeightRegular
<Box sx={{ fontWeight: 'medium' }}>…</Box>  // 解析为 theme.typography.fontWeightMedium
<Box sx={{ fontWeight: 500 }}>…</Box>       // 数值 500 直接透传
<Box sx={{ fontWeight: 'bold' }}>…</Box>    // 解析为 theme.typography.fontWeightBold

对应 Demo 见 FontWeight.js

关于映射细节:fontWeightthemeKeytypography,当你传入字符串 'light' 时,解析器先在 theme.typography.light 中查找;默认主题并不存在这一键,此时会触发 style.ts 中的 alternate prop 回退逻辑——把查找键改写为 fontWeight + 首字母大写的词尾,最终命中 theme.typography.fontWeightLight。这就是上面注释 // theme.typography.fontWeightLight 的来源。而纯数字 500 不会走字符串路径查询,会直接作为 CSS font-weight: 500 输出,因此你完全可以在两者之间混用。

七、Font size:字号、默认字号与点路径

fontSize 支持三种用法:'default'(取主题默认字号)、主题点路径(如标题层级的字号)、直接数值。

<Box sx={{ fontSize: 'default' }}>…</Box>   // 解析为 theme.typography.fontSize
<Box sx={{ fontSize: 'h6.fontSize' }}>…</Box> // 点路径访问 theme.typography.h6.fontSize
<Box sx={{ fontSize: 16 }}>…</Box>           // 数值 16,直接以 px 生效

对应示例见 FontSize.js

其中 'default' 同样依赖 alternate prop 回退:先尝试 theme.typography.default(默认不存在),随后回退为 theme.typography.fontSize'h6.fontSize' 则是点路径能力的直接体现——即便主题中 h6 本身是一个包含多条字体属性的对象,你也可以只取其中 fontSize 字段(参考 getPath. 分隔路径的解析,见 style.ts)。这一点让系统 Prop 在复用主题局部值时非常灵活,例如对齐某块文字的标题字号但保留其自身字重。数值型的 16 因为没有匹配主题键会原样进入 CSS,浏览器按 16px 解释。

八、Font style:字型风格

fontStyle 用来切换常规/斜体等字形,取值与 CSS 对齐:

<Box sx={{ fontStyle: 'normal' }}>…</Box>
<Box sx={{ fontStyle: 'italic' }}></Box>
<Box sx={{ fontStyle: 'oblique' }}></Box>

示例见 FontStyle.js。需要说明的是:虽然 API 表中 fontStyle 的 theme key 为 typography,但在默认主题下不存在 theme.typography.italic 之类的键,因此这些字符串最终会走"查找失败 → 原值透传"路径,作为标准 CSS font-style 值输出;若你在主题中自定义了相关键,也可让它走主题解析。

九、Font family:字体族与主题字体

fontFamily 同时支持主题引用与直接书写字体栈:

<Box sx={{ fontFamily: 'default' }}>…</Box>     // 解析为 theme.typography.fontFamily
<Box sx={{ fontFamily: 'Monospace' }}>…</Box>   // 作为 CSS 字体值透传(通用字体族)

Demo 见 FontFamily.js

'default' 的解析路径与字重 token 相同:theme.typography.default 不存在时回退命中 theme.typography.fontFamily,也就是 MUI 默认的主题字体栈(中文场景下常配合自定义主题覆盖为系统字体栈)。任何其他值都会被作为字体名/字体栈原样输出,因此你也可以传入 '"Roboto", "Helvetica", "Arial", sans-serif' 这类完整栈。顺带一提,style.ts 中的注释表明这套 alternate prop 机制在项目内部还支持类似 fontFamilyCode 的兄弟键,方便在同一套 API 下切换不同的字体子集。

十、Letter spacing 与 Line height:字间距与行高

这两者不依赖主题,取值直接映射 CSS:

<Box sx={{ letterSpacing: 6 }}>…</Box>      // letter-spacing: 6px
<Box sx={{ letterSpacing: 10 }}>…</Box>

<Box sx={{ lineHeight: 'normal' }}></Box>  // line-height: normal
<Box sx={{ lineHeight: 10 }}></Box>        // 数值会被作为无单位行高系数

对应 Demo 分别为 LetterSpacing.jsLineHeight.js

两点使用提示:

  • letterSpacing 传入数字会按 px 处理;若需要按 em 缩放,可传入 '0.1em' 形式的字符串,字符串不会经过数值换算;
  • lineHeight 的数值是无单位行高(相对自身字体大小缩放),这是排版中最推荐的行高写法,因为字号变化时行高会自动按比例伸缩;'normal' 则由浏览器根据字体决定。

十一、完整 API 对照表

下节对应原文档的 API 表格。其中 typographyfontFamilyfontSizefontStylefontWeight 均以 theme.typography 为取值来源,其余项取值直接透传为 CSS。完整导入方式:

import { typography } from '@mui/system';
Import name Prop CSS property Theme key
typography typography font-family, font-weight, font-size, line-height, letter-spacing, text-transform typography
fontFamily fontFamily font-family typography
fontSize fontSize font-size typography
fontStyle fontStyle font-style typography
fontWeight fontWeight font-weight typography
letterSpacing letterSpacing letter-spacing none
lineHeight lineHeight line-height none
textAlign textAlign text-align none
textTransform textTransform text-transform none

值得注意 API 与源码的对应关系(见 typography.ts):表中除 typography 之外的八个单项在源码中都有同名、同 Prop 的具名导出(如 fontFamilyfontSizefontWeight),它们既能作为聚合函数 typography 的一部分被 sx 使用,也能按需单独 import。同时该模块还导出了 TypographyProps 类型,供你在扩展组件 props 类型时复用这些 Prop 的联合定义。由于每个导出本质上都是 (props) => styleObject 的样式函数,从源码结构看,你还可以把聚合结果传给 styled(),快速打造一个自带整套排版能力的自定义组件(类似 Box 的做法)。

十二、响应式与组合实战

因为每个取值都经过 handleBreakpoints(见 style.ts),所有排版 Prop 都能像 sx 的其他属性一样传入断点对象,实现"小屏居中、大屏左对齐"这类典型排版需求:

<Box
  sx={{
    textAlign: { xs: 'center', md: 'left' }, // 移动端居中,桌面端左对齐
    typography: { xs: 'body2', sm: 'body1' }, // 断点间切换排版层级
  }}
>
  响应式排版内容
</Box>

你也可以把多个排版 Prop 与间距、颜色等其他系统功能组合在同一个 sx 里,快速完成一条"完整排版":

import Box from '@mui/material/Box';

<Box
  component="p"
  sx={{
    typography: 'subtitle2',
    fontWeight: 'medium',
    letterSpacing: 1,
    textAlign: 'justify',
    textTransform: 'uppercase',
    m: 1,
  }}
>
  组合使用多个排版工具
</Box>

需要强调:上述所有写法都建立在 theme.typography 与组件 sx 之上,因此换肤与可访问性收益是全局的——不同主题只要调整 typography 对象,所有用语义 token 书写的文字样式就会同步更新,无需改动业务组件。

十三、小结

Typography 系统工具把文字排版收敛为一套"主题优先、语义化取值、一行 sx 搞定"的 API:

  • 层级复用typography: 'body1' 等值直接继承 theme.typography 的完整层级;
  • 主题联动:字重、字号、字体族的语义字符串(light/default/h6.fontSize…)通过点路径与 alternate prop 机制解析到主题 token;
  • 直接透传:对齐、大小写、行高、字间距等与主题无关的能力,取原值映射 CSS;
  • 响应式免费:全部支持 { xs, sm, md, ... } 断点对象写法。

在 Material UI 项目中,这套工具是介于"手写内联样式"与"完整 <Typography> 语义组件"之间的理想中间层。建议在使用时优先选择语义化取值以维持主题一致性,并在真正需要语义标签与自动字号映射的场合,再让 <Typography variant> 出场。若想进一步探索主题排版对象的结构与所有默认值,可查看上文引用的 typography.ts 及各目录下的可运行 Demo,并在你自己的主题中覆盖 typography 键进行验证。

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