Skip to content

Antd自定义主题色 ​

(1) 创建主题状态Store ​

  • 文件: src/store/theme.tsx
tsx
import { proxy } from "valtio";

interface ThemeState {
  /** 当前主题色 */
  colorPrimary: string;
  /** 是否为暗黑模式 */
  isDark: boolean;
  /** 设置主题色 */
  setColorPrimary: (color: string) => void;
  /** 切换暗黑模式 */
  toggleDark: () => void;
}

export const themeStore = proxy<ThemeState>({
  colorPrimary: "#1677ff", // 默认主题色:antd 蓝色
  isDark: false, // 默认亮色模式
  setColorPrimary(color: string) {
    this.colorPrimary = color;
  },
  toggleDark() {
    this.isDark = !this.isDark;
  },
});

(2) 根组件绑定 antd ConfigProvider ​

src/App.tsx ​

tsx
import { RouterProvider } from "react-router-dom";
import { ConfigProvider, theme } from "antd";
import zhCN from "antd/locale/zh_CN";
import { useSnapshot } from "valtio";

import router from "@/routes/router";
import { themeStore } from "@/store/theme";

export default function App() {
  const { colorPrimary, isDark } = useSnapshot(themeStore);

  return (
    <ConfigProvider
      locale={zhCN}
      theme={{
        token: {
          colorPrimary, // ← 关键:绑定到 store 中的 colorPrimary
        },
        algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
      }}
    >
      <RouterProvider router={router} />
    </ConfigProvider>
  );
}

关键机制 ​

  • useSnapshot(themeStore) 让 App 组件"监听" store
  • 当themeStore.colorPrimary 被修改时,App 重新渲染
  • App 重新渲染时,ConfigProvider 的theme prop 更新
  • antd 内部检测到colorPrimary 变化, 自动重新计算所有组件的样式

(3) NavHeader 添加颜色选择器和暗黑模式 ​

  • 文件: src/components/NavHeader/index.tsx
tsx
import {
  MenuFoldOutlined,
  DownOutlined,
  MenuUnfoldOutlined,
} from "@ant-design/icons";
import {
  Breadcrumb,
  Switch,
  Dropdown,
  type MenuProps,
  Space,
  ColorPicker,
} from "antd";
import { useState } from "react";
import { NavHeaderWrapper, NavLeft, NickName } from "./navheader.css";

import { useSnapshot } from "valtio";

import { collapseStore } from "@/store/collapse";
import { themeStore } from "@/store/theme";

import { useNavigate } from "react-router-dom";

function NavHeader() {
  // 组件内订阅状态
  const { collapsed } = useSnapshot(collapseStore);
  const { isDark, colorPrimary } = useSnapshot(themeStore);
  const navigate = useNavigate();

  const [breadlist] = useState([
    { title: "首页", key: "1" },
    { title: "欢迎页", key: "2" },
  ]);

  const items: MenuProps["items"] = [
    {
      key: "1",
      label: "邮箱:2993222085@qq.com",
    },

    {
      key: "2",
      danger: true,
      label: "退出登陆",
    },
  ];

  return (
    <NavHeaderWrapper>
      <NavLeft>
        {collapsed ? (
          <MenuUnfoldOutlined onClick={() => collapseStore.toggle()} />
        ) : (
          <MenuFoldOutlined onClick={() => collapseStore.toggle()} />
        )}
        <Breadcrumb items={breadlist} style={{ marginLeft: 12 }} />
      </NavLeft>
      <div
        style={{
          display: "flex",
          alignItems: "center",
          justifyContent: "flex-end",
        }}
      >
        <ColorPicker
          value={colorPrimary}
          onChange={(color) => themeStore.setColorPrimary(color.toHexString())}
          style={{ marginRight: 12 }}
        />
        <Switch
          checkedChildren="暗黑"
          unCheckedChildren="默认"
          style={{ marginRight: 12 }}
          checked={isDark}
          onChange={() => themeStore.toggleDark()}
        />
        <Dropdown
          menu={{
            items,
            onClick: ({ key }) => {
              if (key === "2") {
                navigate("/login");
                localStorage.removeItem("token");
                return;
              }
              console.log(`当前序号:${key},hello world`);
            },
          }}
          trigger={["click"]}
        >
          <a onClick={(e) => e.preventDefault()}>
            <NickName>
              <Space>
                七宗罪
                <DownOutlined />
              </Space>
            </NickName>
          </a>
        </Dropdown>
      </div>
    </NavHeaderWrapper>
  );
}

export default NavHeader;

(4) NavFooter 使用动态主题色 ​

  • 修改 src/components/NavFooter/index.tsx
tsx
import { useSnapshot } from "valtio";

import { FooterLink, FooterWrapper, Gutter } from "./navfooter.css";

import { themeStore } from "@/store/theme";

function NavFooter() {
  const { colorPrimary } = useSnapshot(themeStore); // 订阅主题色

  return (
    <FooterWrapper
      style={{ "--theme-color": colorPrimary } as React.CSSProperties}
    >
      <div>
        <FooterLink href="https://www.uiyin.com" target="_blank">
          个人博客页面
        </FooterLink>
        <Gutter>|</Gutter>
        <FooterLink href="#" target="_blank">
          React后台管理系统
        </FooterLink>
        <Gutter>|</Gutter>
        <FooterLink href="https://demo1.jsopy.com/" target="_blank">
          Vue3后台管理系统
        </FooterLink>
        <Gutter>|</Gutter>
      </div>
      <div>Copyright @ 2026 七宗罪React测试项目 All Rights Reserved.</div>
    </FooterWrapper>
  );
}

关键代码 ​

tsx
style={{ '--theme-color': colorPrimary } as React.CSSProperties}
  • --theme-color 是一个 CSS 自定义属性(CSS 变量)
  • 通过style 属性设置在FooterWrapper 这个 DOM 元素上
  • as React.CSSProperties 是 TypeScript 类型断言,因为 React 默认不认识--xxx 这种自定义属性

修改 src/components/NavFooter/navfooter.css.tsx ​

tsx
import styled from "@emotion/styled";

export const FooterWrapper = styled.div`
  text-align: center;
  line-height: 30px;
  color: #b0aeae;
  font-size: 14px;

  a {
    color: #78b2f8;
  }
`;

export const FooterLink = styled.a`
  &:hover {
    color: var(--theme-color) !important; // ← 关键:使用 CSS 变量
  }
`;

export const Gutter = styled.span`
  margin-left: 10px;
  margin-right: 10px;
`;
  • 为什么必须用css变量

注意

因为themeColor 是 硬编码的常量 ,在代码打包后就固定了。而var(--theme-color) 是在浏览器运行时,从 DOM 元素的style 属性中动态读取的。当colorPrimary 变化时,FooterWrapper 上的--theme-color 值会更新,FooterLink 的 hover 颜色也随之更新

最后完成流程图 ​

bash
┌─────────────────────────────────────────────────────────────────┐
│                        用户操作层                                │
│  ┌─────────────┐    ┌─────────────┐                            │
│  │ ColorPicker │    │   Switch    │                            │
│  │  选择颜色    │    │  切换暗黑   │                            │
│  └──────┬──────┘    └──────┬──────┘                            │
└─────────┼──────────────────┼────────────────────────────────────┘
          │                  │
          ▼                  ▼
┌─────────────────────────────────────────────────────────────────┐
│                      状态管理层 (valtio)                         │
│              themeStore.setColorPrimary('#xxx')                  │
│              themeStore.toggleDark()                             │
│                         │                                       │
│              ┌─────────┴─────────┐                              │
│              ▼                   ▼                              │
│        colorPrimary 变化      isDark 变化                        │
│              │                   │                              │
└──────────────┼───────────────────┼──────────────────────────────┘
               │                   │
               ▼                   ▼
┌─────────────────────────────────────────────────────────────────┐
│                      组件渲染层                                  │
│  ┌─────────────────┐    ┌─────────────────┐                     │
│  │    App.tsx      │    │  NavFooter      │                     │
│  │  useSnapshot()  │    │  useSnapshot()  │                     │
│  │        │        │    │        │        │                     │
│  │        ▼        │    │        ▼        │                     │
│  │  ConfigProvider │    │ FooterWrapper   │                     │
│  │  colorPrimary   │    │ --theme-color   │                     │
│  │  algorithm      │    │                 │                     │
│  └────────┬────────┘    └────────┬────────┘                     │
│           │                      │                              │
│           ▼                      ▼                              │
│  ┌─────────────────┐    ┌─────────────────┐                     │
│  │  antd 组件自动   │    │ FooterLink hover │                    │
│  │  更新主色调      │    │ 读取 var(--theme-color)                │
│  │  (Button等)     │    │                 │                     │
│  └─────────────────┘    └─────────────────┘                     │
└─────────────────────────────────────────────────────────────────┘

总结 每个文件的作用 ​

文件职责
store/theme.tsx数据中心 :存储colorPrimary 和isDark ,提供修改方法
App.tsxantd 桥接 :订阅 store,通过ConfigProvider 把颜色传给所有 antd 组件
NavHeader/index.tsx用户入口 :提供 ColorPicker 和 Switch,让用户可以修改 store
NavFooter/index.tsx自定义组件 :订阅 store,通过 CSS 变量把颜色传给 styled-components
NavFooter/navfooter.css.tsx样式定义 :用var(--theme-color) 读取 CSS 变量,实现动态 hover 颜色