LEEUNITYAssets / Content / 博客.asset
层级
⌄ Lee / Homepage
INSPECTOR
Type
Content
Name
博客
lee@mtftm.com ↗GitHub ↗
N005FRONT / VP FONT SWITCH

技术 / English

Vitepress Navbar Font Switching

Principle of Implementation

更新
2026.07.18
阅读
2 分钟
语言
English

Principle of Implementation

Define a CSS variable and update the CSS variable when the navbar button is clicked

Add Button

// docs/.vitepress/config.ts
// nav ...
{
    text: "Switch Font",
    items: [
      {
        text: "LXGW WenKai",
        link: "#", // Since no jump is needed, we use an empty link
      },
      {
        text: "LXGW Neo XiHei",
        link: "#",
      },
    ],
  },

Import Fonts in CSS

@font-face {
  font-family: 'LXGW WenKai';
  src: url('/LXGWWenKai-Regular.ttf') format('truetype');
}

Introduce CSS Variables in CSS

Since the initial value of --main-font is empty before being assigned, we can use fallback font options as the default font

:root {
/* var(--main-font, 'Fallback Font') */
--vp-font-family-base: var(--main-font, 'LXGW WenKai');/* Text font */
--vp-font-family-mono: "LXGW WenKai Mono";/* Code font */
}

Listen to Button and Pass Font Name to CSS Variable

Here we create a new JS script to listen for button clicks and update the CSS variable

// docs/.vitepress/theme/fontSwitcher.js
export const fontMap = {
  'LXGW WenKai': "'LXGW WenKai', serif", // 'Button Name':'Font Name', Fallback font (optional)
  'LXGW Neo XiHei': "'LXGW Neo XiHei', serif",
  };

  // Font switching function
  export const switchFont = (font) => {
    document.documentElement.style.setProperty('--main-font', fontMap[font]);
  };

  // Add global font switching event listener
  export const addFontSwitchListener = () => {
    // Normally, in Vitepress, dropdown content is wrapped in a span tag, please replace with ('.items span'), in this site, dropdowns are wrapped in a tag so use a
    const fontSwitchItems = document.querySelectorAll('.items a'); // Select all 'a' tags in navbar items
    // The following log is for debugging, if not effective you can uncomment the code below
    // console.log(`Found ${fontSwitchItems.length} font switching items`);
    fontSwitchItems.forEach(item => {
      item.addEventListener('click', (e) => {
        e.preventDefault();
        const target = e.target;
        const selectedFont = target.innerText; // Get the clicked font name
        // console.log(`${selectedFont}`);
        switchFont(selectedFont); // Switch font
      });
    });
};

The above is an incorrect example. In Vitepress mobile view, the collapsed navbar is by default in a folded state. Only when the user expands it, the relevant resources are loaded; but the listener is created before this event, which leads to it not being able to listen successfully.

Correct Example

export const fontMap = {
  // Font mapping table
  'LXGW WenKai': 'LXGW WenKai',
  'LXGW WenKai Mono': 'LXGW WenKai Mono',
  'LXGW Neo XiHei': 'LXGW Neo XiHei',
  'NeoXiHei Code': 'NeoXiHei Code',
  'Default Font': 'system-ui',
  'Sarasa UI SC': 'Sarasa UI SC',
  'Source Han Serif CN': 'Source Han Serif CN',
  'Sans': 'sans',
  'Serif': 'serif',
};

// Font switching function
export const switchFont = (font) => {
  document.documentElement.style.setProperty('--main-font', fontMap[font]);
};

// Add global font switching event listener
export const addFontSwitchListener = () => {
  // Select hamburger menu
  const hamburger = document.querySelector('.VPNavBarHamburger');
  const fontSwitchItems = document.querySelectorAll('.items a'); // Select all 'a' tags in navbar items
      // console.log(`Found ${fontSwitchItems.length} font switching items`);

      fontSwitchItems.forEach(item => {
        item.addEventListener('click', (e) => {
          e.preventDefault();
          const target = e.target;
          const selectedFont = target.innerText; // Get the clicked font name
          // console.log(`${selectedFont}`);
          switchFont(selectedFont); // Switch font
        });
      });

  // Add hamburger menu event listener
  if (hamburger) {
    hamburger.addEventListener('click', () => {
      // Add font switching event listener when hamburger menu opens
      const fontSwitchItems = document.querySelectorAll('.items a'); // Select all 'a' tags in navbar items
      // console.log(`Found ${fontSwitchItems.length} font switching items`);

      fontSwitchItems.forEach(item => {
        item.addEventListener('click', (e) => {
          e.preventDefault();
          const target = e.target;
          const selectedFont = target.innerText; // Get the clicked font name
          // console.log(`${selectedFont}`);
          switchFont(selectedFont); // Switch font
        });
      });
    });
  }
};

Import Script in index.ts/index.js

// docs/.vitepress/theme/index.ts
// https://vitepress.dev/guide/custom-theme
import PtjsTheme from '@project-trans/vitepress-theme-project-trans/theme'
import { onMounted } from 'vue'
import { addFontSwitchListener } from './fontSwitcher' // Import script

import 'uno.css'
import './style.css'

export default {
  extends: PtjsTheme, // This is my imported custom theme
  setup() {
    onMounted(() => {
      addFontSwitchListener(); // Add font switching event listener
    });
  },
}

Conclusion

If it does not work, use the console to check whether the css file access path is correct, whether the script runs properly, and whether the button content is correctly obtained (see comments)