Frontend Development 11 min read

Mastering Theme Switching: 8 Proven Methods to Implement Dark/Light Modes in Web Apps

This article explores eight practical techniques for implementing theme switching in web applications—including class toggling, stylesheet swapping, CSS variables, alternate rel links, Less modifyVars, Ant Design/Vant custom themes, Element‑UI dynamic theming, and JSS—detailing code snippets, advantages, and limitations of each approach.

Xueersi 1-on-1 Technology Team
Xueersi 1-on-1 Technology Team
Xueersi 1-on-1 Technology Team
Mastering Theme Switching: 8 Proven Methods to Implement Dark/Light Modes in Web Apps

Theme switching can be achieved in many ways; choose the solution that best fits your project.

1. Switch styles via class

CSS:

<code>body {
  background: #000000;
}
body.theme-light {
  background: #000000;
}
body.theme-dark {
  background: #ffffff;
}</code>

HTML:

<code>&lt;html class="theme-light"&gt;
  &lt;body&gt;
    theme
  &lt;/body&gt;
&lt;/html&gt;</code>

JS:

<code>document.documentElement.className = 'theme-dark';</code>

Note: Large stylesheet files can be hard to maintain.

2. Prepare multiple stylesheets and change link href via JS

CSS files:

<code>// theme-light.css
body { background: #000000; }

// theme-dark.css
body { background: #ffffff; }</code>

HTML:

<code>&lt;link id="theme" href="theme-light.css" rel="stylesheet" type="text/css"&gt;</code>

JS:

<code>document.getElementById('theme').href = 'theme-dark.css';</code>

Drawback: Requires maintaining multiple stylesheets and may cause a delay when switching.

3. Use CSS variables for theming

Define variables in theme files:

<code>// theme/light.css
:root {
  --bg-color: #000000;
  --text-color: #ffffff;
}

// theme/dark.css
:root {
  --bg-color: #ffffff;
  --text-color: #000000;
}</code>

HTML:

<code>&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;link id="theme" rel="stylesheet" type="text/css" href="css/theme/light.css"&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id="app"&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</code>

Use variables in components:

<code>.content {
  color: var(--text-color, #000000);
  background-color: var(--bg-color, #ffffff);
}</code>

JS handler:

<code>onThemeChange(name) {
  document.getElementById('theme').setAttribute('href', `css/theme/${name}.css`);
}</code>

In production, consider using

css-vars-ponyfill

for compatibility.

4. Alternate stylesheet via rel attribute

<code>&lt;link href="theme-light.css" rel="stylesheet" type="text/css" title="light"&gt;
&lt;link href="theme-dark.css" rel="alternate stylesheet" type="text/css" title="dark"&gt;</code>

Firefox supports switching through the UI. For other browsers, toggle the

disabled

attribute:

<code>&lt;link href="theme-light.css" rel="stylesheet" type="text/css" title="light" disabled&gt;
&lt;link href="theme-dark.css" rel="alternate stylesheet" type="text/css" title="dark"&gt;</code>

5. Browser‑side Less modifyVars method

Include Less and configuration in

&lt;head&gt;

:

<code>&lt;link rel="stylesheet/less" type="text/css" href="styles.less" /&gt;
&lt;script&gt;
  less = {
    env: "development",
    async: false,
    fileAsync: false,
    poll: 1000,
    functions: {},
    dumpLineNumbers: "comments",
    relativeUrls: false,
    rootpath: ":/a.com/"
  };
&lt;/script&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/less.js/3.11.1/less.min.js"&gt;&lt;/script&gt;</code>

Theme change handler:

<code>onThemeChange (color) {
  less.modifyVars({
    '@primary-color': color
  })
  .then(() => {
    console.log(`修改主题 ${color} 成功`);
  });
};</code>

Suitable for development environments or user‑customizable themes; production should use pre‑compiled CSS.

6. Ant Design / Vant theme customization (Less)

Define global variables:

<code>@primary-color: #1890ff; // main color
@success-color: #52c41a; // success
@warning-color: #faad14; // warning
@error-color: #f5222d; // error</code>

Override via

modifyVars

in

vue.config.js

(Less loader 6):

<code>module.exports = {
  css: {
    loaderOptions: {
      less: {
        lessOptions: {
          modifyVars: {
            'primary-color': '#40A9FF',
            'success-color': '#56d41a',
            'warning-color': '#fabd14',
            'error-color': '#f6232d'
          },
          javascriptEnabled: true,
        },
      },
    },
  },
};</code>

7. Element‑UI dynamic theming

Define color formulas (formula.json):

<code>{
  "shade-1": "color(primary shade(10%))",
  "light-1": "color(primary tint(10%))",
  "light-2": "color(primary tint(20%))",
  "light-3": "color(primary tint(30%))",
  "light-4": "color(primary tint(40%))",
  "light-5": "color(primary tint(50%))",
  "light-6": "color(primary tint(60%))",
  "light-7": "color(primary tint(70%))",
  "light-8": "color(primary tint(80%))",
  "light-9": "color(primary tint(90%))"
}</code>

Map original colors to variable names:

<code>const colorMap = {
  '#409eff': 'primary',
  '#3a8ee6': 'shade-1',
  '#53a8ff': 'light-1',
  '#66b1ff': 'light-2',
  '#79bbff': 'light-3',
  '#8cc5ff': 'light-4',
  '#a0cfff': 'light-5',
  '#b3d8ff': 'light-6',
  '#c6e2ff': 'light-7',
  '#d9ecff': 'light-8',
  '#ecf5ff': 'light-9'
};</code>

After loading the full Element‑UI CSS, replace all color literals with the variable names to obtain

originalStyle

. When a user selects a theme color (e.g.,

#24BA9C

), generate a palette:

<code>this.colors = {
  primary: "#24BA9C",
  shade-1: "rgb(32, 167, 140)",
  light-1: "rgb(58, 193, 166)",
  light-2: "rgb(80, 200, 176)",
  light-3: "rgb(102, 207, 186)",
  light-4: "rgb(124, 214, 196)",
  light-5: "rgb(146, 221, 206)",
  light-6: "rgb(167, 227, 215)",
  light-7: "rgb(189, 234, 225)",
  light-8: "rgb(211, 241, 235)",
  light-9: "rgb(233, 248, 245)"
};</code>

Replace variables in the style template and update the

&lt;style&gt;

tag:

<code>let cssText = this.originalStyle;
Object.keys(this.colors).forEach(key => {
  cssText = cssText.replace(new RegExp('(:|\s+)' + key, 'g'), '$1' + this.colors[key]);
});
// ...
document.head.lastChild.innerText = cssText;</code>

8. JSS dynamic theming (React)

<code>import React from "react";
import { render } from "react-dom";
import { createUseStyles, ThemeProvider, useTheme } from "react-jss";

const useStyles = createUseStyles({
  wrapper: {
    padding: 40,
    background: ({ theme }) => theme.background,
    textAlign: "left"
  },
  title: {
    font: {
      size: 40,
      weight: 900
    },
    color: ({ theme }) => theme.color
  },
  link: {
    color: ({ theme }) => theme.color,
    "&:hover": {
      opacity: 0.5
    }
  }
});

const Comp = () => {
  const theme = useTheme();
  const classes = useStyles({ theme });
  return (
    <div className={classes.wrapper}>
      <h1 className={classes.title}>Hello React-JSS!</h1>
      <a className={classes.link} href="http://cssinjs.org/react-jss" target="_blank">See docs</a>
    </div>
  );
};

const theme = {
  background: "#f7df1e",
  color: "#24292e"
};

const App = () => (
  <ThemeProvider theme={theme}>
    <Comp />
  </ThemeProvider>
);

render(<App />, document.getElementById("root"));</code>

Related Links

Alternative style sheets: https://developer.mozilla.org/enUS/docs/Web/CSS/Alternative_style_sheets

Ant Design Vue custom theme: https://www.antdv.com/docs/vue/customize-theme-cn/

Vant custom theme: https://youzan.github.io/vant/#/zh-CN/theme#jie-shao

Element‑UI theme preview: https://github.com/ElementUI/theme-chalk-preview

FrontendJavaScriptCSSCSS variablesdark modeLesstheme switchingJSS
Xueersi 1-on-1 Technology Team
Written by

Xueersi 1-on-1 Technology Team

Official account of the Xueersi 1-on-1 Technology Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.