定制主题 for css in js

264 阅读3分钟

背景

由于新公司 产品logo没有想好,所以希望我们的产品可以定制主题色。

主题色配置,这个功能对我来说听起来既陌生又熟悉,就好像一直有接触,但是第一笔又不知道怎么下。 不过读了几篇相关文章后,思路也渐渐明朗,最终完成。那么先一起看看主题配置有几种实现方案呢?

自定义主题方案

通过《前端主题切换方案》,我们知道主题色配置的方案其实不止一种

我来做个简单的归纳

方案一

通过link标签引入 dark.css or light.css 

拿到主题之后,动态引入指定css

虽然是按需加载css,但是缺点很明显:

  1. 样式优先级问题,后期的样式很可能会覆盖导致不生效
  2. 异步加载css,可能会造成渲染不流畅
  3. 样式写死,如果再来一种主题,会很麻烦

只因为第一,二条,就可以直接pass了

方案二:

主题样式和 类名对应,可以提前加载所有主题css,通过修改类名来切换主题,

这个方案呢,也会有样式优先级问题,所以也不考虑了

方案三:

Css变量方式,root定义全局变量

通过class区分主题,修改全局变量值

元素通过var 动态获取全局变量,动态设置主题色

这个方案好,不存在优先级问题,可以考虑

方案四~方案六我不再赘述,本质上还是利用css变量,然后我们写样式的时候去拿动态色值变量去渲染

最终方案确定

好了,随着各种排除法我们的方案也顺势而出:

就是用动态变量去写样式,那么我们的配置表应该长什么样子呢?

是root定义吗?还是js对象呢?

我的想法还是倾向于用js对象来定义主题配置表,是基于以下考虑

  • 首先,我们的项目的css风格是css in js ,天然对js 对象友好,所以没理由不用js对象

  • 其次,root定义的全局变量虽然可以全局访问,但是结构过于单一,不像js对象有层级结构

所以我们最终的方案是:

提前开发两套 主题配色表,考虑到样式定制的 粒度细分程度 ,及参考antd, 数据结构大致如下:

const Theme = {

  colorPrimary: '#1677ff',

  Button: {

    colorPrimary: '#00B96B',

  },

至于这套配置如何让所有组件都能访问到?

我们通过 styled-components 的 ThemeProvider, 可以将定义的主题样式注入到组件树中其下方任意位置的所有样式组件中

import {ThemeProvider} from "styled-components";

// theme: 一个对象,将作为 theme 注入到组件树下样式组件的所有插值中。
<ThemeProvider theme={{ color: 'green' }}>
  <Box>ThemeProvider</Box>
</ThemeProvider>

const Box = styled.div`
  color: ${props => props.theme.color};
`

我的求助时间

image.png

我在写样式的时候发现一个问题,styled只有第二个参数传函数时,props才能拿到theme,有人知道为什么吗?在线等!!


const MemberTabWrap = styled(Flex)(() => styles.memberTabWrap)
const MemberTabWrap = styled(Flex)(styles.memberTabWrap) // 拿不到 props.theme

const memberTabWrap = css`
    margin: 0 .15rem;
    background: ${props => {return props?.theme?.token?.colorPrimary}};
`

好了,这次的小小分享就到这里,欢迎大家与我交流,解答彼此的疑问,我们一起进步!