在字节跳动内部业务高速发展的背景下,设计与研发团队面临着一个共同的挑战:如何在海量的中后台应用开发中,保证设计语言的一致性,同时又不失灵活性?如何让设计资源与代码资产形成真正的闭环,而不是各自为政?Semi Design的诞生就是为了解决这一核心问题。
Semi Design是抖音前端与UED团队联合打造的一款现代、全面、灵活的设计系统和UI库。它不仅仅是一个React组件库,更是一整套包含了设计语言、组件实现、主题定制、设计工具链等完整生态的解决方案。从抖音集团内部数百个中后台应用的实际业务场景出发,经过层层打磨和验证,Semi Design已正式开源,成为企业级应用的优选方案。
本文将从Semi Design的核心理念入手,系统全面地介绍其组件体系、主题定制、暗色模式、国际化、D2C能力等知识模块,结合大量代码示例和实际场景,帮助读者在实际项目中用好Semi Design。
一、Semi Design概述
1.1 什么是Semi Design
Semi Design是由字节跳动抖音前端与UED团队设计、开发并维护的现代、全面、灵活的设计系统和UI库。它包含了设计语言、React组件库、主题系统、设计工具链(DSM)、设计稿转代码(D2C)等完整生态,是一套开箱即用的企业级中后台解决方案。
Semi Design的核心设计理念可以概括为三个关键词:
1.2 Semi与其他UI库的对比
二、Design Token与主题定制
Semi Design的主题定制能力是其最核心的优势之一。它提供了2800多个设计令牌(Design Tokens),覆盖了从颜色、字体、间距到阴影、圆角、动画等所有视觉维度。
2.1 CSS变量与SCSS变量体系
Semi Design采用CSS自定义属性(CSS Variables)作为主题系统的基础,所有设计变量都通过CSS变量暴露给开发者。这种机制使得主题定制变得简单而强大:
变量覆盖:开发者可以通过覆盖CSS变量的方式修改主题样式
作用域隔离:变量修改可以限定在特定组件或区域
运行时动态更新:CSS变量支持运行时动态修改,无需重新加载页面
/* 通过CSS变量覆盖自定义主题 */
.semi-custom-theme {
--semi-color-primary: #4f46e5;
--semi-color-primary-hover: #6366f1;
--semi-color-primary-active: #4338ca;
--semi-border-radius-small: 4px;
--semi-border-radius-medium: 8px;
--semi-border-radius-large: 12px;
}
2.2 使用Vite插件定制主题
Semi提供了官方的Vite插件@douyinfe/vite-plugin-semi,支持多种主题定制方式:
// vite.config.js
import { semiPlugin } from '@douyinfe/vite-plugin-semi';
import path from 'path';
export default {
plugins: [
semiPlugin({
// 方式一:通过npm包定制(推荐,由Semi DSM生成)
theme: '@douyinfe/semi-theme-default',
// 方式二:通过本地SCSS文件覆盖
include: path.join(__dirname, 'custom-theme.scss'),
// 方式三:直接传递变量键值对
variables: {
'$font-size-small': '14px',
'$color-primary': '#4f46e5'
},
// 可选:替换CSS前缀(默认为semi)
prefixCls: 'custom'
})
]
});
2.3 设计系统管理工具(DSM)
Semi Design提供了完整的设计系统管理工具,包括:
Semi DSM:可视化主题定制平台,可以实时预览并导出主题包
C2D(Code to Design):支持将代码主题同步到Figma设计稿
D2C(Design to Code):支持将Figma设计稿一键转换为代码
2.4 ColorPicker色彩选择器
当需要让用户自定义主题色时,Semi的ColorPicker组件可以配合CSS变量实现动态主题切换:
import { ColorPicker, Button, Space } from 'semi-ui';
import { useState } from 'react';
function ThemeCustomizer() {
const [primaryColor, setPrimaryColor] = useState('#0066ff');
const applyTheme = (color) => {
document.documentElement.style.setProperty('--semi-color-primary', color);
// 根据主色自动生成配套颜色
const hoverColor = adjustColor(color, -10);
const activeColor = adjustColor(color, -20);
document.documentElement.style.setProperty('--semi-color-primary-hover', hoverColor);
document.documentElement.style.setProperty('--semi-color-primary-active', activeColor);
};
return (
<div>
<ColorPicker value={primaryColor} onChange={(color) => {
setPrimaryColor(color);
applyTheme(color);
}} />
<div className="preview-area">
<Button type="primary">预览按钮</Button>
</div>
</div>
);
}