Skip to content

主题切换

该项目主题色分为:全局主题色 和 预览区主题色,分别采用两种不同的解决方案。

  • 全局主题色:采用 styled-componentscreateGlobalStyle 来创建全局样式,通过 styled-componentsThemeProvider 来提供主题色。
  • 预览区主题色:采用 css 变量来实现,方便用户后期自定义主题色。

全局主题色

定义主题色

ts
// src/theme/theme.ts 全局主题色
export const lightTheme = {
  background: '#ffffff',
  color: '#000000',
  borderColor: '#e6e6e6',
  silderHelpTitleColor: '#3f4a54',
  silderHelpTextColor: '#959da5',
  statusColor: '#333333',
  toolbarHoverBg: '#e6e6e6',
  scrollbarThumbBgColor: '#d9d9d9',
  scrollbarTrackBgColor: '#f5f5f5'
}

export const darkTheme = {
  background: '#191d24',
  color: '#dfdfdf',
  borderColor: '#464646',
  silderHelpTitleColor: '#bac6d2',
  silderHelpTextColor: '#bac6d2',
  statusColor: '#dfdfdf',
  toolbarHoverBg: '#464646',
  scrollbarThumbBgColor: '#898989',
  scrollbarTrackBgColor: '#898989'
}

类型文件 styled.d.ts,给 styled-components 提供 ts 类型提示

ts
import 'styled-components'

declare module 'styled-components' {
  export interface DefaultTheme {
    background: string
    color: string
    borderColor: string
    silderHelpTitleColor: string
    silderHelpTextColor: string
    statusColor: string
    toolbarHoverBg: string
    scrollbarThumbBgColor: string
    scrollbarTrackBgColor: string
  }
}

通过 styled-components 中的 ThemeProvider,创建全局样式组件

tsx
// src/theme/global-theme.tsx
import React, { FC } from 'react'
import { ThemeProvider } from 'styled-components'
import { lightTheme, darkTheme } from './theme'

const GlobalTheme: FC<{
  children: React.ReactNode
  theme?: 'light' | 'dark'
}> = ({ children, theme = 'light' }) => {
  return (
    <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
      {children}
    </ThemeProvider>
  )
}

export default GlobalTheme

例如,样式写法:

tsx
const Container = styled.div`
  /* border: 1px solid #e6e6e6; */
  border: 1px solid ${(props) => props.theme.borderColor};
  /* background-color: #fff; */
  background-color: ${(props) => props.theme.background};
`

预览区主题色

定义变量

ts
// src/theme/preview-theme.ts 预览区主题色
export const previewTheme = {
  light: {
    color: '#333',
    'special-color': '#3f4a54',
    'inlincode-color': '#3594f7',
    'inlincode-bg-color': 'rgba(59, 170, 250, 0.1)',
    'border-color': '#e6e6e6',
    'blockquote-color': '#3f4a54',
    'blockquote-border-color': '#d0d7de',
    'table-border-color': '#ddd',
    'th-bg-color': '#f2f2f2',
    'table-hover-bg-color': '#f5f5f5'
  },
  dark: {
    color: '#fff',
    'special-color': '#bac6d2',
    'inlincode-color': '#c9d1d9',
    'inlincode-bg-color': '#2d3339',
    'border-color': '#2d2d2d',
    'blockquote-color': '#8b949e',
    'blockquote-border-color': '#444c56',
    'table-border-color': '#30363d',
    'th-bg-color': '#252525',
    'table-hover-bg-color': '#222222'
  }
}

创建预览区主题 hook

原理:在根节点添加 css 变量的方式来实现主题切换

tsx
// src/hooks/use-preview-theme.tsx
import { previewTheme } from '@/theme/preview-theme'
import { useEffect } from 'react'

export const usePreviewTheme = (themeProps: 'light' | 'dark') => {
  useEffect(() => {
    const theme =
      themeProps === 'light' ? previewTheme.light : previewTheme.dark

    // 设置 CSS 变量
    const root = document.body
    Object.entries(theme).forEach(([key, value]) => {
      root.style.setProperty(`--md-preview-${key}`, value)
    })
  }, [themeProps])
}

例如:

css
.mini-md-inline-code {
    line-height: 22px;
    padding: 2px 4px;
    /* color: #3594f7; */
    color: var(--md-preview-inlincode-color);
    /* background-color: rgba(59, 170, 250, 0.1); */
    background-color: var(--md-preview-inlincode-bg-color);
    border-radius: 4px;
}