前端实现切换主题方案

更新日期: 2022-10-05阅读: 2.1k标签: 主题

前端开发人员面临着制作增强用户体验和适应用户偏好的用户界面的任务。带有 css 的 react 主要可用于创建多色可切换主题。为用户提供了在给定时间点在主题颜色之间切换以适合他们的偏好的特权。


介绍

在本文中,我们将讨论使用 react、less 和其他依赖项构建多色主题的各种步骤和要求。目前市面上用的多的就两种方案:

  • React-styled-components
  • 全局维护两套主题色(假如只有 light 和 dark 两种主题)以及全局的 root 变量以供组件引用,配合自定义 context 实现不同主题切换功能

个人不喜欢 styled-components 这种方法,样式、结构、脚本混在一起感觉有点别扭,感兴趣的可以自己去尝试,本文主要介绍第二种方法,并且配合 react 比较流行的组件库antd 进行开发。


前置知识

读者应该有 react、less 和他们选择的任何设计库的经验。如果您对上述语言或框架不满意,请在继续之前花点时间了解基础知识。


React 中主题处理程序的组件

要实现构建可切换多色主题的目标,您的 Web 应用程序必须具备以下组件:

  • 用于颜色状态管理的 Context api
  • 导航栏 (主题切换按钮)
  • 页面元素的设计依赖项
  • 不同主题的全局 less 文件


架构图



开始使用 React 进行主题开发

第 1 步:创建 React 应用程序

每次开发的第一步react都是创建react应用程序。这一步很容易实现。要创建您的react应用程序,请在命令终端中运行如下所示的命令:

npx create-react-app demo-app
# or
yarn create-react-app demo-app

上面的 command 代码片段将创建您的 react 应用程序,安装默认的开发依赖项,并最终提供一个样板来开始您的应用程序开发。

第 2 步:编译 Antd 两种主题下的 less 文件

antd 主题配置大多数情况下我们会在 webpack 配置文件中配置对应 less loader 修改其中的 less 变量来实现基础的主题色等的自定义需求,其中 antd 还给我们提供了一种配置的 context api ,可以对组件配置国际化,自定义主题等功能。其中自定义主题(样式)可以通过修改内置的样式来实现,默认情况下,antd 组件内的样式名都是 ant 开头,可以通过官方提供的 ConfigProvider 来修改这一配置, 即修改 prefixCls 属性。这样我们可以通过运行时动态修改样式名前缀(白天模式跟黑夜模式各设置一个前缀)来实现白天跟黑夜模式的切换。antd 已经把 dark 模式的样式给我们了,现在,我们先来把两套样式文件提取出来放在我们项目中自己维护,这也是为了我们更加方便地去修改我们自定义所需要的颜色而不用通过修改全局样式去覆盖原有样式,这样即不用担心样式层级问题,也降低了维护成本。

注意:如果未安装less,则需要安装less:npm install -g less

进入项目根目录,执行以下命令:

# 白天模式
lessc --js --modify-var="ant-prefix=custom-light" node_modules/antd/dist/antd.variable.less custom-light.css

# 黑夜模式
lessc --js --modify-var="ant-prefix=custom-dark" node_modules/antd/dist/antd.variable.less custom-dark.css

执行完会在项目根目录(脚本执行时的目录)生成两份 css 文件,为了后续开发方便,可以把这两份 css 后缀改为 less。如图:


其中,global.less 用于保留全局样式变量

第 3 步:创建主题上下文

在 theme/context 目录下新建一个用于处理全局 theme mode 变量、模式初始化、以及模式切换逻辑的 Provider,其中重要的逻辑有以下三点:

1.初始化当前用户保存的模式,从本地缓存中查找,没有的话走最初始用户没设置过的逻辑——跟随当前用户系统的模式,重要知识点:

// 匹配浏览器的主题
window.matchMedia('(prefers-color-scheme: light)')

// 获取初始主题模式
const getThemeMode = () => {
  return window.matchMedia('(prefers-color-scheme: light)').matches
    ? 'light'
    : 'dark'
}

// 监听浏览器(系统)主题变化
const matchMediaHandler = window.matchMedia('(prefers-color-scheme: light)')
matchMediaHandler.addEventListener('change', changeColorMode)

2.每次用户手动切换主题后就跟随用户习惯走

const localKey = 'theme-mode'

const getStorage = () => {
  const value = localStorage.getItem(localKey)
  return value ? value : getThemeMode()
}

3.暴露给开发人员当前的 mode 以及切换 mode 的 switch方法

const switchModeHandler = (value: string) => {
  const doc = document.querySelector('body')
  if (doc) {
    doc.removeAttribute('class')
    doc.classList.add(value)
    setStorage(value)
    setMode(value)
  }
}

const contextValue: ThemeContextType = {
  mode: mode,
  switchMode: switchModeHandler
}

4.包裹 antd 提供的 ConfigProvider ,根据 mode 的变幻动态传值修改 prefixCls 的值以动态修改组件内的样式

return (
  // 自定义全局维护的主题
  <ThemeContext.Provider value={contextValue}>
    {/*antd 的主题*/}
    <ConfigProvider prefixCls={`custom-${mode}`}>
      {props.children}
    </ConfigProvider>
  </ThemeContext.Provider>
)

完整代码如下:

import React, { createContext, useEffect, useState } from 'react'
import { ConfigProvider } from 'antd'

const localKey = 'theme-mode'

const getStorage = () => {
  const value = localStorage.getItem(localKey)
  return value ? value : getThemeMode()
}

const setStorage = (value: string) => {
  localStorage.removeItem(localKey)
  localStorage.setItem(localKey, value)
}

const getThemeMode = () => {
  return window.matchMedia('(prefers-color-scheme: light)').matches
    ? 'light'
    : 'dark'
}

// CONTEXT
interface ThemeContextType {
  mode: string
  switchMode: (value: string) => void
}

export const ThemeContext = createContext<ThemeContextType>({
  mode: '',
  switchMode: () => {}
})

// PROVIDER
export const ThemeContextProvider: React.FC<{
  children: JSX.Element
}> = props => {
  const [mode, setMode] = useState('')

  useEffect(() => {
    const changeColorMode = () => {
      const color = getThemeMode()
      switchModeHandler(color)
    }

    const matchMediaHandler = window.matchMedia('(prefers-color-scheme: light)')
    matchMediaHandler.addEventListener('change', changeColorMode)

    switchModeHandler(getStorage())

    return () =>
      matchMediaHandler.removeEventListener('change', changeColorMode)
  }, [])

  const switchModeHandler = (value: string) => {
    const doc = document.querySelector('body')
    if (doc) {
      doc.removeAttribute('class')
      doc.classList.add(value)
      setStorage(value)
      setMode(value)
    }
  }

  const contextValue: ThemeContextType = {
    mode: mode,
    switchMode: switchModeHandler
  }

  return (
    // 自定义全局维护的主题
    <ThemeContext.Provider value={contextValue}>
      {/*antd 的主题*/}
      <ConfigProvider prefixCls={`custom-${mode}`}>
        {props.children}
      </ConfigProvider>
    </ThemeContext.Provider>
  )
}

第 4 步:创建UI界面


第 5 步:提供界面组件需要的全局样式变量

在 theme/styles/global.less 新增如下变量:

:root {
  --light-color: #fff;
  --dark-color: #000;
}

body {
  &.light {
    --default-bg: var(--light-color);
    --default-text-color: var(--dark-color);
  }

  &.dark {
    --default-bg: var(--dark-color);
    --default-text-color: var(--light-color);
  }
}

应用全局样式变量,如 NavBar 组件的样式文件中(其他组件同理):

.container {
  background: var(--default-bg);
    color: var(--default-text-color);
}

第 6 步:切换主题

在 NavBar 组件中:

import React, { type FC, useContext } from 'react'
import cls from 'classnames'

import { SearchBar } from '@common/components'
import { ThemeContext } from '@src/theme/context'

import styles from './index.module.less'

const NavBar: FC = () => {
 
  const { mode, switchMode } = useContext(ThemeContext)

  return (
    <div
      className={cls(styles.container, styles[mode])}
    >
      <div className={styles.relativeWrap}>
        <div
          className={styles.themeSwitch}
          onClick={() => switchMode(mode === 'dark' ? 'light' : 'dark')}
        >
          <img
            src={
              mode === 'dark'
                ? 'https://assets.blocksec.com/image/1664446020484-3.svg'
                : 'https://assets.blocksec.com/image/1664446020484-2.svg'
            }
            alt=""
          />
        </div>
      </div>
    </div>
  )
}

export default NavBar


结论

在本文中,我们应用了各种概念来使用 React 创建多主题。我们还使用react-context-api.

本文涵盖的概念可用于未来涉及颜色和主题管理的项目。

来自:https://segmentfault.com/a/1190000042573976

链接: https://fly63.com/article/detial/12183

wordpress大前端DUX主题

DUX主题是基于WordPress程序的主题,由themebetter团队原创开发,是目前比较火的wordpress主题;支持百度熊掌号,适用于垂直站点、科技博客、个人站,扁平化设计、简洁白色、超多功能配置

Vue实现换主题/皮肤功能

网站换肤,主题切换。网站的主题色可以在几种常用颜色之间进行切换,还有相关图片、图标也要跟随主题进行切换。文章由两部分组成:css切换,图片图标切换

Web前端主题切换的几种方案

本文将介绍 Web 前端主题切换的几种常用方案,示例代码基于 React 框架。废话少说,show you the code!这种方案利用了css多层样式精确匹配的特点,通过样式覆盖的方式实现主题的切换。

采用CSS3的VAR实现动态主题切换

官方称呼其为自定义属性,坊间通常叫做css变量,因为它类似于其他js语言中的变量,可用于存储颜色、字体、大小宽度等css属性值;定义方式为两个连字符也就是中划线(--)开头,引用方式为var(custom-property-name, value)

前端 “一键换肤“ 的几种方案

现在越来越多的网站都提供了拥有换肤(切换主题)功能,如 ElementUI,既是为了迎合用户需求,或是为了凸显自己特点,因此提供了个性化定制的功能.

Sass应用之实现主题切换

实现主题切换有几种不同的方案,比如使用CSS变量,使用JavaScript动态加载对应的主题样式文件等。本文主要讲的是如何使用Sass实现主题切换。

使用CSS变量实现主题定制真的很简单

Varlet是通过css变量来组织样式的,什么是css变量呢,其实很简单,首先声明自定义的css属性,随便声明在哪个元素上都可以,不过只有该元素的后代才能使用,所以如果要声明全局所有元素都能使用的话,可以设置到根伪类:root下

常用Vue UI框架的主题切换

在如今,很多网页已经可以手动切换明亮模式和黑暗模式,网页的主题切换已经成为了一个常用的需求,因此,本文将从常见框架的处理方式总结一些相关的操作,并探究其本质。

兼容ie浏览器 网站变灰色

在遇到特殊情况的时候,我们作为站长需要紧急将网站变灰的需求,大家都知道通过简单的CSS样式即可实现,上述代码配合指定网页在IE内核下的渲染模式,就可以达到效果,这个语句意思便是告诉IE浏览器

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!