Rebass 实战指南:基于 Styled System 的 React 原始 UI 组件库

原创2026-09-27 05:34:0111 阅读
文章标签:UI组件前端设计系统

Rebass 实战指南:基于 Styled System 的 React 原始 UI 组件库

导读

Rebass 是一套用 React 构建的"原始(primitive)"UI 组件库,底层由 Styled System 驱动,目标是以极小的学习成本帮助团队快速起步搭建设计系统。本文以仓库根目录的 README.md 为主线,结合 packages/ 下各组件的源码与测试用例,系统讲解 Rebass 的安装、核心组件、设计原则、主题化(Theming)与扩展机制。读完本文,你将掌握如何用 Box、Flex、Heading、Button 等基础组件组合出真实业务界面,并理解其数组式响应式语法、sx 属性与 variant 变体机制的底层原理。

Rebass 是什么

Rebass 的定位在项目描述中写得很清楚:React primitive UI components built with Styled System。也就是说,它提供的不是开箱即用的"成品 UI 库",而是一组语义最简、样式无强观点的基础组件,让开发者可以在其上自由组合、按设计规范定制,从而"不用把整个海洋煮沸(Start your design system without boiling the ocean)"。

仓库采用 Lerna + Yarn workspaces 管理多包结构,packages/ 下包含了:

  • rebass——核心组件包(Text、Heading、Button、Link、Image、Card 等),当前版本 4.0.7(见 packages/rebass/package.json);
  • reflexbox——布局基座(Box、Flex),Rebass 所有组件都构建在它之上;
  • preset / preset-material——默认与 Material 风格的主题预设;
  • forms——Label、Input、Select、Textarea、Radio、Checkbox、Switch、Slider 等表单组件;
  • layout——Tiles 等栅格/布局组件;
  • space——不产生包裹 DOM 的间距组件;
  • docs——官网文档站(Gatsby + MDX);
  • bundler——用于构建发布产物的打包工具。

安装与快速上手

核心包的安装非常简单:

npm i rebass

(仓库为多包 workspace,安装各子包同理,如 npm i @rebass/forms、npm i @rebass/preset。)

安装后即可直接使用,README 给出的最小示例是一个由 Box、Heading、Button 组成的问候卡片:

import React from 'react'
import { Box, Heading, Button } from 'rebass'

export default props =>
  <Box>
    <Heading>Hello</Heading>
    <Button>Rebass</Button>
  </Box>

这里体现了两条核心体验:组件是极简的原始块(Box 默认渲染 div),样式通过属性直接声明(Styled System props,如 p、m、width、color、bg 等)。

响应式数组语法

Rebass 支持 Styled System 的移动端优先数组语法:给样式属性传数组,数组的每一项对应一个断点(默认 theme.breakpoints),实现零媒体查询的响应式布局:

// 最小屏宽 100%,下一断点 50%,再下一断点 25%
<Box width={[ '100%', '50%', '25%' ]} />

这种语法同样适用于 p、m、fontSize 等所有样式属性与 sx 属性。断点宽度可通过主题中的 theme.breakpoints 数组自定义,其默认约定可参考 reflexbox 的 README。

核心组件:从 Box 到 Card

Rebass 的六个核心组件都定义在 packages/rebass/src/index.js 中,它们的共同特征是:全部是 Box 的扩展,并统一使用 forwardRef 转发 ref。组件结构可用下面这张关系图概括:

Box / Flex(来自 reflexbox)
  └── Text(tx='text',渲染 div)
  └── Heading(as='h2',tx='text',variant='heading')
  └── Link(as='a',variant='link')
  └── Button(as='button',tx='buttons',variant='primary')
  └── Image(as='img')
  └── Card(variant='card')

下面逐一展开各组件的行为,均以源码与 packages/rebass/test/index.js 中的测试断言为依据。

Box 与 Flex(来自 Reflexbox)

Box 与 Flex 由 packages/reflexbox/src/index.js 实现:

export const Box = styled('div', {
  shouldForwardProp
})({
  boxSizing: 'border-box',
  margin: 0,
  minWidth: 0,
},
  base,
  variant,
  sx,
  props => props.css,
  compose(
    space,
    layout,
    typography,
    color,
    flexbox,
  ),
)

export const Flex = styled(Box)({
  display: 'flex'
})
  • Box 默认渲染 div,但通过 as 属性可渲染任意元素(测试用例 <Box as='header' /> 断言渲染为 header);
  • 内置 box-sizing: border-box; margin: 0; min-width: 0,从源头规避 Flexbox 布局中的经典坑;
  • 通过 compose 组合了 Styled System 的 space、layout、typography、color、flexbox 五组样式能力;
  • Flex 只是给 Box 加上 display: flex,其余能力完全继承。

Text

export const Text = forwardRef((props, ref) =>
  <Box ref={ref} tx='text' {...props} />
)

Text 是纯文本容器,声明了 tx='text',意味着它的 variant 会从主题的 text 键中查找。测试用例展示了它的用法:<Text textAlign='center' fontWeight='bold' fontStyle='italic' /> 会分别生成 text-align、font-weight、font-style 规则;配合主题 text.caps 变体可实现全大写排版。

Heading

export const Heading = forwardRef((props, ref) =>
  <Box
    ref={ref}
    as='h2'
    tx='text'
    variant='heading'
    {...props}
    __css={{
      fontSize: 4,
      fontFamily: 'heading',
      fontWeight: 'heading',
      lineHeight: 'heading',
    }}
  />
)

Heading 默认渲染为 h2,并通过 __css 声明默认排版:fontSize: 4(对应预设 fontSizes 的第 5 项,即 24px,见测试断言)、字体系列/字重/行高均引用主题键 heading。使用 as 可以切换为 h1、h3 等其他标题级别。

Button

export const Button = forwardRef((props, ref) =>
  <Box
    ref={ref}
    as='button'
    tx='buttons'
    variant='primary'
    {...props}
    __css={{
      appearance: 'none',
      display: 'inline-block',
      textAlign: 'center',
      lineHeight: 'inherit',
      textDecoration: 'none',
      fontSize: 'inherit',
      px: 3,
      py: 2,
      color: 'white',
      bg: 'primary',
      border: 0,
      borderRadius: 4,
    }}
  />
)

Button 默认渲染为原生 button 元素,tx='buttons' + variant='primary' 表示默认样式来自主题的 buttons.primary;__css 提供了按钮必备的基线样式(重置 appearance、去掉边框、主题色背景等)。测试还验证了 <Button as='a' /> 可渲染为链接,方便实现"按钮外观、链接行为"的场景。

Link、Image、Card

  • Link:as='a' + variant='link',默认样式从主题 variants.link 读取(预设中为 color: 'primary');
  • Image:as='img',__css 内置 maxWidth: '100%' 与 height: auto,保证图片响应式且不变形;
  • Card:默认 variant='card',样式完全交给主题 variants.card 定义;测试用例展示了通过 p、bg 属性与 sx 属性组合出带圆角、投影的卡片。

特性解读(Features)

README 对 Rebass 的特性概括为以下八点,我们结合仓库逐一展开:

特性 含义与仓库依据
快速起步,不用"煮沸整个海洋" 只提供原始组件,不捆绑业务样式;Getting Started 文档(packages/docs/src/pages/getting-started.mdx)演示了用 Box/Card/Image/Heading/Text 五分钟拼出产品卡片
设计约束与用户自定义尺度 所有样式值走主题 scale(space、fontSizes、colors 等),禁止随意像素值散落各处;见 preset 主题
Styled System props 带来一流开发体验 空格、布局、排版、颜色、Flexbox 五组属性全部可用(reflexbox 源码)
主题化一等公民,完全兼容 Theme UI 遵循 [system-ui theme specification] 生态,ThemeProvider 可互换使用(详见下文"主题化"一节)
数组语法实现移动端优先响应式 见上文"响应式数组语法"
Box + Flex 提供 Flexbox 布局 布局基座来自 reflexbox
高设计与开发速度的内置灵活性 as 换元素、variant 换风格、sx 写局部样式,组合自由度极高
体积约 4KB README 声明的最小体积,配合多包按需安装进一步控制首屏负担

设计原则:Minimal · Useful · Unopinionated

README 明确了 Rebass 的七个设计原则——Minimal(最小)、Useful(有用)、Unopinionated(无观点)、Flexible(灵活)、Consistent(一致)、Extensible(可扩展)、Themeable(可主题化),并引用 Unix 哲学"做一件事,并把它做好"。

这套原则在源码中处处可见:

  • 最小:核心组件源码总共不足百行,每个组件只做"元素 + 默认变体 + 基线样式"三件事;
  • 无观点:Heading 不强制 h1,Button 不绑定主题外的特殊样式,默认值全部可被 props 覆盖;
  • 可扩展:组件全部由 Box 派生且支持 forwardRef,意味着你可以 const Card = props => <Box variant='card' {...props} /> 式地自由组合(官方也提供了专门的 扩展指南文档);
  • 可主题化:所有可变量均通过主题键(text、buttons、variants)寻址,而不是写死在组件里。

主题化(Theming)

ThemeProvider 的使用

Rebass 组件默认不带任何主题样式,需要在应用根部用 ThemeProvider 注入主题。官方推荐的组合是 @rebass/preset 预设主题 + emotion-theming 的 ThemeProvider:

npm i @rebass/preset emotion-theming
import React from 'react'
import { ThemeProvider } from 'emotion-theming'
import theme from '@rebass/preset'

export default props =>
  <ThemeProvider theme={theme}>
    {props.children}
  </ThemeProvider>

如果使用 Theme UI 生态,也可以换成 theme-ui 的 ThemeProvider 或 gatsby-plugin-theme-ui。

默认预设主题的结构

@rebass/preset 源码 展示了完整的主题结构,它是理解 Rebass 一切样式行为的起点:

export const preset = {
  colors: {
    text: '#000',
    background: '#fff',
    primary: '#07c',
    secondary: '#30c',
    muted: '#f6f6f9',
    gray: '#dddddf',
    highlight: 'hsla(205, 100%, 40%, 0.125)',
  },
  fonts: {
    body: 'system-ui, sans-serif',
    heading: 'inherit',
    monospace: 'Menlo, monospace',
  },
  fontSizes: [12, 14, 16, 20, 24, 32, 48, 64, 96],
  fontWeights: { body: 400, heading: 700, bold: 700 },
  lineHeights: { body: 1.5, heading: 1.25 },
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  sizes: { avatar: 48 },
  radii: { default: 4, circle: 99999 },
  shadows: { card: '0 0 4px rgba(0, 0, 0, .125)' },
  text: { heading: {…}, display: {…}, caps: {…} },
  variants: { avatar: {…}, card: {…}, link: {…}, nav: {…} },
  buttons: { primary: {…}, outline: {…}, secondary: {…} },
  styles: { root: {…} },
}

关键点:

  • space 数组决定 p={3} = 16px、m={2} = 8px 等取值;
  • fontSizes 数组决定 fontSize={4} = 24px(Heading 默认值);
  • buttons.primary 决定 Button 默认外观;
  • variants.card 决定 Card 默认外观,text.caps 等决定 Text 变体。

仓库还提供了另一套 Material 风格预设(@rebass/preset-material,标注为 work-in-progress),使用 Roboto 字体体系与 Material Design 色板。

自定义主题与 Variants

遵循 Theme Specification] 的主题都可以直接使用,例如在 [入门文档 中演示的自定义主题——定义自己的 fontSizes、colors 与 buttons 变体:

const theme = {
  fontSizes: [12, 14, 16, 24, 32, 48, 64],
  colors: {
    primary: '#07c',
    gray: '#f6f6ff',
  },
  buttons: {
    primary: { color: 'white', bg: 'primary' },
    outline: {
      color: 'primary',
      bg: 'transparent',
      boxShadow: 'inset 0 0 0 2px'
    },
  },
}

然后通过 variant 属性切换:

<Button variant='primary' mr={2}>Beep</Button>
<Button variant='secondary'>Boop</Button>

variant 机制的底层实现在 reflexbox 源码 中:组件通过 get(theme, tx + '.' + variant, get(theme, variant)) 依次在 tx 指定的主题键(如 buttons、text)与全局 variants 键中查找样式对象;sx 属性则通过 css(props.sx)(props.theme) 编译为样式。这意味着:变体是主题驱动、可继承的(如 preset 中 buttons.outline 通过 variant: 'buttons.primary' 继承 primary 的基线样式再覆盖颜色)。

sx 与 css 属性的分工

  • sx:主题感知的样式属性,支持引用主题 scale 的值(color: 'primary'、p: 4 等),适合写局部设计;
  • css:不透传主题转换的原始样式属性,适合需要"退出主题系统"的场合(reflexbox 的 README 明确说明了两者的分工)。

布局与响应式:Reflexbox 与 Tiles

Rebass 的布局能力全部来自 reflexbox 的 Box/Flex。reflexbox 自称"the original Box component since 2015",是原 Reflexbox、Grid Styled 与 Rebass Grid 三库 API 整合的产物(见 packages/reflexbox/README.md)。

一个典型的两栏响应式布局:

import React from 'react'
import { Flex, Box } from 'rebass'   // 或从 'reflexbox' 引入

export default props =>
  <Flex flexWrap='wrap'>
    <Box width={[ 1, 1/2 ]} p={3}>Reflex</Box>
    <Box width={[ 1, 1/2 ]} p={3}>Box</Box>
  </Flex>

Reflexbox 支持五组 Styled System props,完整映射表见 reflexbox README,这里给出常用摘录:

分组 属性示例 主题键
Space m mt mr mb ml mx my / p pt pr pb pl px py space
Layout width height minWidth maxWidth minHeight maxHeight sizes
Typography fontFamily fontSize fontWeight lineHeight letterSpacing fonts fontSizes fontWeights lineHeights letterSpacings
Color color bg opacity colors
Flexbox alignItems justifyContent flexWrap flexDirection flex order 等 N/A(直接映射 CSS)

使用 Styled Components 的团队,可改从 reflexbox/styled-components 导入同一套 API(reflexbox 通过 NODE_ENV=styled 的构建脚本生成 Styled Components 版本,见 packages/reflexbox/package.json)。

此外,@rebass/layout 提供了 Tiles 组件,基于 CSS Grid 实现自适应栅格:传入 width 自动生成 repeat(auto-fit, minmax(...)),传入 columns 生成 repeat(n, 1fr),并支持响应式数组值。

扩展组件包

表单组件(@rebass/forms)

@rebass/forms 提供了可访问(accessible)且可主题化的表单组件:Label、Input、Select、Textarea、Radio、Checkbox、Switch、Slider。它们同样构建在 Box/Flex 之上,通过 tx='forms' + variant 接入主题。一个完整表单示例见 packages/forms/README.md,涵盖 Label htmlFor 关联、Radio/Checkbox 嵌套在 Label 中、以及用 Flex + mx={-2} 实现负 margin 栅格间距等技巧。

无包裹间距组件(@rebass/space)

@rebass/space 解决"只想给子元素加间距、不想多一层 div"的问题。它通过 StyledChildren 将样式类名直接克隆到每个子元素上,自身不渲染 DOM:

import Space from '@rebass/space'

const App = props => (
  <Space mx={3} my={[ 2, 3 ]}>
    <h1>Hello</h1>
    <h2>Hi</h2>
    <button>Beep</button>
  </Space>
)

支持的属性与 space 工具一致(m/mt/mr/mb/ml/mx/my 及对应 p 系列,取值可为数字、字符串或数组),完整表格见 packages/space/README.md。

工程质量与测试保障

仓库对代码质量有严格要求,根目录 package.json 中 Jest 覆盖率阈值设置为 branches/functions/lines/statements 均为 90%。测试主要位于 packages/*/test/ 下:

本地运行测试的方式(仓库根目录):

yarn install
yarn test

开发文档站点:yarn start(Gatsby dev server);构建站点:yarn build。

从 v3 迁移与版本历史

README 提示从 v3 升级的用户参考官网 Migration Guide(仓库内对应文档为 packages/docs/src/pages/migrating.mdx)。同时 README 列出了历史版本入口(v3.2.2、v2.3.2、v1.0.7),便于查阅旧版行为。当前核心包版本为 4.0.7,仅依赖 reflexbox@^4.0.6,依赖面极窄(packages/rebass/package.json)。

相关生态与延伸阅读

Rebass 处于 Styled System 生态的中间层,README 将相关项目分为两组:

  • 上游依赖:Styled System(样式 props 引擎);
  • 平级协作:Theme UI(主题/风格化框架)、Emotion、Styled Components(底层样式引擎,Rebass 默认基于 Emotion,同时为 Styled Components 提供构建产物)。

仓库内的文档站点提供了更深入的主题化(theming.mdx)、Props 速查(props.mdx)、组件扩展(extending.mdx)以及 CSS Grid 布局等实践指南,均为可继续深入阅读的仓库内一手资料。

小结

Rebass 的核心价值在于"少而精":以 Styled System 的 props 体系为引擎,以 reflexbox 的 Box/Flex 为布局基座,以主题 scale + variant 变体为约束机制,再辅以数组式响应式语法,构成了一个可快速起步、可深度定制、体积精简(约 4KB)的原始组件库。本文梳理的源码、测试与文档路径,均可作为后续实践与二次开发的直接参照。

本文内容均以当前仓库 README.md 及各包源码、测试、配置文件为事实依据。

登录后查看全文
rebass