element-plus主题配置及动态切换主题

0 阅读3分钟

创建vue项目的不同方式

pnpm create vite 使用于创建一些组件库,第三方库的时候

pnpm create vue 适用于vue项目,内部有一些基础的样式,vue的主题,

element-plus主题配置

按需引入element

1. scss变量自定义主题

分支:feature-element-theme-anxu-scss

src/styles/element/index.scss

/* just override what you need */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': green,
    ),
  )
);

vite.config.ts

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ElementPlus from 'unplugin-element-plus/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [
        ElementPlusResolver({
          importStyle: 'sass',
        }),
      ],
    }),
    ElementPlus({
      useSource: true,
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/element/index.scss" as *;`,
      },
    },
  },
})

按需引入组件时,使用组件,会自动引入对应组件的样式,不用再main.ts中引入elememnt的全部样式。

主题配置只需要修改需要改变的颜色变量即可

可以看到,对应组件的颜色变量已经变化

image.png

2. css变量自定义主题

分支: feature-element-theme-anxu-css

src/styles/element/index.scss

:root {
  --el-color-primary: green;
}

vite.config.ts

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ElementPlus from 'unplugin-element-plus/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [
        ElementPlusResolver({
          importStyle: 'sass',
        }),
      ],
    }),
    ElementPlus({
      useSource: true,
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/element/index.scss" as *;`,
      },
    },
  },
})

可以看到,css变量被注入到组件对应的scss文件顶部,从而让主题生效

image.png

全量引入element

scss全量引入

分支 feature-element-theme-all-scss

/* just override what you need */
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': green,
    ),
  )
);

// If you just import on demand, you can ignore the following content.
// 如果你是全量导入,需要加下边这句,如果是按需引入,请注释掉下边这句
@use 'element-plus/theme-chalk/src/index.scss' as *;

main.ts

import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import './styles/element/index.scss'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.use(ElementPlus)
app.mount('#app')

这时候可以看到样式被全部引入了

image.png

css全量引入

分支:feature-element-theme-all-css src/styles/element/index.css

:root {
  --el-color-primary: green;
}

main.ts

import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import './styles/element/index.css'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.use(ElementPlus)
app.mount('#app')

可以看到,element的css变量已经被覆盖了

image.png

动态主题

运行时动态切换主题,不能依赖scss变量

原因

SCSS 变量是编译时变量,在构建阶段就被替换掉了,在浏览器运行时无法再改变,所以无法用于“动态切换主题”。

动态主题应该使用css变量来实现

官方提供了一个切换主题的项目github.com/element-plu…

里面引入了一套暗黑模式的样式,然后通过 useToggle进行切换

分支 feature-element-theme-anxu-scss-dynamic 还是采用按需引入elememnt的方式

src/styles/index.scss 这里全量引入暗黑模式的样式

// import dark theme
@use 'element-plus/theme-chalk/src/dark/css-vars.scss' as *;

// :root {
//   --ep-color-primary: red;
// }

body {
  font-family:
    Inter, system-ui, Avenir, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
    'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  margin: 0;
}

a {
  color: var(--ep-color-primary);
}

main.ts在main.ts中引入样式

// import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import '@/styles/index.scss'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

src/styles/element/index.scss 这里是自定义默认主题变量

$--colors: (
  'primary': (
    'base': rgb(0, 128, 19),
  ),
  'success': (
    'base': #21ba45,
  ),
  'warning': (
    'base': #f2711c,
  ),
  'danger': (
    'base': #db2828,
  ),
  'error': (
    'base': #db2828,
  ),
  'info': (
    'base': #42b8dd,
  ),
);

// You should use them in scss, because we calculate it by sass.
// comment next lines to use default color
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  // do not use same name, it will override.
  $colors: $--colors // $button-padding-horizontal: ('default': 50px)
);

// if you want to import all
// @use "element-plus/theme-chalk/src/index.scss" as *;

// You can comment it to hide debug info.
// @debug $--colors;

// custom dark variables
@use './dark.scss';

src/styles/element/dark.scss 这是对暗黑主题颜色重新定义

// only scss variables

$--colors: (
  'primary': (
    'base': #589ef8,
  ),
);

@forward 'element-plus/theme-chalk/src/dark/var.scss' with (
  $colors: $--colors
);

vite.config.ts 这里引入自定义主题变量,自定义默认主题和暗黑主题颜色

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ElementPlus from 'unplugin-element-plus/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [
        ElementPlusResolver({
          importStyle: 'sass',
        }),
      ],
    }),
    ElementPlus({
      useSource: true,
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/element/index.scss" as *;`,
      },
    },
  },
})

动态切换主题

  • 通过useDark获取当前是否是暗黑模式
  • 通过useToggle切换主题
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark()
const toggleDark = useToggle(isDark)
const changeTheme = () => {
  toggleDark()
}
</script>

<template>
  <header>
    <el-button type="primary" @click="changeTheme"> 切换主题 </el-button>
    <el-input></el-input>
  </header>

  <RouterView />
</template>

<style scoped>
header {
  line-height: 1.5;
  max-height: 100vh;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

nav {
  width: 100%;
  font-size: 12px;
  text-align: center;
  margin-top: 2rem;
}

nav a.router-link-exact-active {
  color: var(--color-text);
}

nav a.router-link-exact-active:hover {
  background-color: transparent;
}

nav a {
  display: inline-block;
  padding: 0 1rem;
  border-left: 1px solid var(--color-border);
}

nav a:first-of-type {
  border: 0;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }

  nav {
    text-align: left;
    margin-left: -1rem;
    font-size: 1rem;

    padding: 1rem 0;
    margin-top: 1rem;
  }
}
</style>

代码仓库地址 github.com/Stacey1018/…