Implementing One‑Click Theme Switching with SCSS in a Vue Project
This article explains how to build a one‑click skin‑changing system for a Vue application by defining SCSS variables, creating theme maps, configuring webpack and vue.config.js to inject global styles, and using both CSS custom properties and advanced SCSS features such as loops, maps, and mixins to dynamically switch colors, fonts, and sizes.
During front‑end development it is common to need dynamic changes such as swapping site theme colors, adjusting font sizes, or updating banner icons without rewriting CSS; the article shows how to meet these requirements by establishing a complete SCSS variable system and using it throughout the project.
The final effect allows users to select a theme and see the UI instantly reflect the chosen colors; the article also displays the project directory layout.
src
├── App.vue
├── main.js
├── router
│ └── index.js
├── store
│ └── index.js
├── style
│ ├── settings
│ │ └── variable.scss // style variables
│ └── theme
│ ├── default
│ ├── index.scss // theme entry
│ └── old
└── views
├── Home.vue // theme switch page
├── List.vue
└── Mine.vueFirst, the SCSS compilation environment is set up by installing the necessary packages.
npm i sass
// sass‑loader must be a specific version; newer versions cause "this.getOptions" errors
npm i -D [email protected]
npm i -S normalize.cssCommon visual parameters such as primary colors, font sizes, and weights are defined as SCSS variables.
// ./style/settings/variable.scss
$info: #17a2b8 !default;
$danger: #dc3545 !default;
$font-size-base: 1rem !default;
$font-size-lg: $font-size-base * 1.25 !default;
$font-weight-normal: 400 !default;
$font-weight-bold: 600 !default;A theme map groups these variables for each theme (e.g., default and old ) and is imported in a single entry file.
// ./style/theme/index.scss
@import "../settings/variable.scss";
$themes-color: (
default: (
color: $info,
font-weight: $font-weight-normal,
font-size: $font-size-lg,
),
old: (
color: $danger,
font-weight: $font-weight-bold,
font-size: $font-size-slg,
),
);To avoid importing the variables manually in each component, vue.config.js is configured to prepend the theme entry globally.
module.exports = {
css: {
loaderOptions: {
scss: {
additionalData: `@import "@/style/theme/index.scss";`
}
}
}
};Theme switching is performed by setting a data-theme attribute on body (defaulted in App.vue ) and by generating a theme array at build time using a custom webpack plugin.
// vue.config.js (custom plugin)
const fs = require('fs');
const webpack = require('webpack');
const themeFiles = fs.readdirSync('./src/style/theme');
let ThemesArr = [];
themeFiles.forEach(item => {
if (fs.lstatSync(`./src/style/theme/${item}`).isDirectory()) {
ThemesArr.push(item);
}
});
module.exports = {
configureWebpack: config => ({
plugins: [new webpack.DefinePlugin({ THEMEARR: JSON.stringify(ThemesArr) })]
})
};In Home.vue the selected theme index is stored, and the data-theme attribute is updated when the user confirms a new theme.
// Home.vue methods
methods: {
onConfirm(currentTheme) {
this.currentTheme = currentTheme;
this.showPicker = false;
this.currentThemeIndex = this.themeValue.findIndex(t => t === currentTheme);
document.getElementsByTagName('body')[0].setAttribute('data-theme', THEMEARR[this.currentThemeIndex]);
}
}CSS custom properties can also be used directly; selectors such as [data-theme="default"] .t-list-title read the variables defined with --foo and --bar .
/* default.scss */
[data-theme="default"] .t-list-title, [data-theme="default"] .t-list-sub-title, [data-theme="default"] .t-list-info {
color: var(--foo);
font-weight: 400;
font-size: 1rem * 1.25;
}The article also covers advanced SCSS techniques useful for theme generation: @each loops over lists and maps, map-get , map-merge , and the @content directive inside mixins, illustrated with a themify mixin that builds a $theme‑map and provides a themed() function for property lookup.
// ./Home.vue mixin example
@mixin themify() {
@each $theme-name, $map in $themes-color {
[data-theme="#{$theme-name}"] & {
$theme-map: () !global;
@each $key, $value in $map {
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
@content;
}
}
}
@function themed($key) { @return map-get($theme-map, $key); }
.t-list-title, .t-list-sub-title, .t-list-info {
@include themify() {
color: themed("color");
font-weight: themed("font-weight");
font-size: themed("font-size");
}
}All source code is available at https://github.com/AshesOfHistory/test-skin-refresh , and the article concludes that the reader now understands SCSS basics, how to combine them to achieve one‑click skin changes, and the core principles behind dynamic theming.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.