前端样式

XinAdmin 使用 Tailwind CSS v4 + Ant Design 构建样式系统,通过 CSS Layer 控制样式优先级,结合 Ant Design 的 Semantic DOM 实现组件级样式定制。

关于 Ant Design 样式的更多细节,请参阅 Ant Design 样式兼容文档

样式架构

┌──────────────────────────────────────────┐
│              应用样式层                    │
│  Tailwind utilities + 自定义 styles prop   │
│  具有最高优先级,用于覆盖 antd 默认样式       │
├──────────────────────────────────────────┤
│              Ant Design 样式层             │
│  CSS-in-JS 生成的组件样式                   │
│  ConfigProvider theme token 全局控制       │
├──────────────────────────────────────────┤
│              基础样式层                    │
│  Tailwind base(reset + 全局基础样式)       │
└──────────────────────────────────────────┘

一、Tailwind CSS v4

1.1 集成方式

XinAdmin 使用 Tailwind CSS v4 的 Vite 插件 方式集成,无需 tailwind.config 文件:

// vite.config.ts
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),  // Tailwind v4 Vite 插件
  ],
});

1.2 CSS Layer 配置

/* web/index.css */
@layer theme, base, antd, components, utilities;
@import "tailwindcss";
Layer用途优先级
themeCSS 变量和主题定义最低
baseTailwind 基础重置样式
antdAnt Design CSS-in-JS 样式
components自定义组件样式
utilitiesTailwind 工具类最高

关键设计:将 antd 层放在 componentsutilities 之前,确保 Tailwind 工具类和自定义组件样式可以覆盖 Ant Design 的默认样式,无需使用 !important

注意:如果 Ant Design 的 CSS-in-JS 样式没有输出到 antd layer,需要使用 @ant-design/cssinjs<StyleProvider layer> 包裹应用。详见 Ant Design 样式兼容文档

1.3 Tailwind v4 特有特性

项目使用 Tailwind v4 的新特性:

特性示例说明
任意值text-[20px]bg-[#1677ff]直接使用任意值,无需 extend
新工具类backdrop-blur-xssize-14v4 新增的实用工具
@layer 声明@layer antd { ... }自定义 layer 控制优先级

1.4 常用工具类

项目中广泛使用的 Tailwind 工具类:

类别常用类
布局flexgriditems-centerjustify-betweenshrink-0box-border
定位relativestickyz-1z-10z-999
尺寸w-fullh-fullsize-14h-8h-16
间距p-2m-2.5mt-5mb-1.5gap-6pl-5
文字truncatetext-centerfont-semiboldtext-[20px]
圆角rounded-smrounded-lgrounded-xlrounded-full
溢出overflow-hiddenoverflow-y-autooverflow-x-hidden
条件lg:grid-cols-4md:grid-cols-2(响应式前缀)

1.5 Tailwind 与 Ant Design 配合使用

// 使用 Tailwind 类快速调整布局和间距
<Card className="mb-5 p-2.5">
  <Space className="flex justify-between items-center">
    <Button type="primary" className="rounded-lg font-semibold">
      提交
    </Button>
    <Input className="w-60" placeholder="搜索" />
  </Space>
</Card>

二、Ant Design 主题系统

2.1 ConfigProvider 全局主题

通过 AntdProvider 组件统一管理全局主题令牌:

// components/AntdProvider/index.tsx
const theme: ThemeConfig = useMemo(() => ({
  components: {
    Layout: {
      headerHeight: themeConfig.headerHeight,
      bodyBg: themeConfig.bodyBg,
      headerBg: themeConfig.headerBg,
      siderBg: themeConfig.siderBg,
      // ...
    },
    Menu: {
      activeBarBorderWidth: 0,
      itemBg: 'transparent',
      subMenuItemBg: 'transparent',
    },
  },
  token: {
    colorPrimary: themeConfig.colorPrimary,
    colorBgBase: themeConfig.colorBg,
    colorTextBase: themeConfig.colorText,
    borderRadius: themeConfig.borderRadius,
    controlHeight: themeConfig.controlHeight,
    // ... error / success / warning / info / link
  },
  algorithm: themeConfig.algorithm ? algorithm[themeConfig.algorithm] : undefined,
}), [themeConfig]);

2.2 Token 与算法

token说明默认值
colorPrimary品牌主色#1677ff
colorBgBase基础背景色#fff
colorTextBase基础文字色#000
borderRadius全局圆角10
controlHeight控件高度32
algorithm主题算法defaultAlgorithm

四种主题算法:

算法说明
defaultAlgorithm默认亮色算法
darkAlgorithm暗黑模式算法
defaultCompactAlgorithm亮色 + 紧凑
darkCompactAlgorithm暗黑 + 紧凑

2.3 useToken 运行时主题值

在组件中通过 useToken 获取当前主题的具体值,用于动态样式:

import { theme } from 'antd';

const { useToken } = theme;

function MyComponent() {
  const { token } = useToken();

  return (
    <div
      style={{
        background: token.colorPrimaryBg,
        color: token.colorText,
        borderColor: token.colorBorder,
        borderRadius: token.borderRadius,
        boxShadow: token.boxShadow,
      }}
    >
      内容
    </div>
  );
}

项目中 useToken 的使用位置:

  • layout/SettingDrawer.tsx — 主题设置抽屉内的布局缩略图
  • layout/ColumnsMenu.tsx — 分栏菜单的选中高亮色
  • layout/HeaderRightRender.tsx — 搜索弹窗的分割线颜色
  • pages/dashboard/analysis.tsx — 仪表盘卡片和图表颜色

2.4 主题持久化

主题配置通过 Zustand store 持久化到 localStorage

// stores/global/index.ts
const useGlobalStore = create<GlobalStore>()(
  persist(
    (...args) => ({ ...globalState, ...globalAction(...args) }),
    {
      name: 'global-storage',
      storage: createJSONStorage(() => localStorage),
    }
  )
);

用户通过 SettingDrawer 可视化修改主题配置,所有修改通过 400ms 防抖实时生效并自动保存。


三、Ant Design Semantic DOM

3.1 什么是 Semantic DOM

Ant Design v5+ 为每个组件内部元素定义了稳定的语义名称,通过 classNamesstyles props 可以对特定子元素进行精确样式覆盖,无需依赖内部 DOM 类名。

// 使用 Semantic DOM 精确样式覆盖
<Input
  classNames={{
    root: 'my-custom-input-root',        // 组件根元素
    input: 'my-custom-input-inner',       // 输入框本体
    prefix: 'my-custom-input-prefix',     // 前缀区域
    suffix: 'my-custom-input-suffix',     // 后缀区域
  }}
  styles={{
    root: { marginBottom: 16 },
    input: { fontSize: 14, color: '#333' },
    prefix: { color: '#999' },
  }}
/>

3.2 常见组件语义名称

组件语义名称说明
Buttonroot, icon, contenticon 含 loading 图标
Inputroot, prefix, suffix, input, count
Input.Search以上 + buttonbutton 可进一步嵌套
Input.Password以上 + suffixsuffix 含可见性切换
InputNumberroot, prefix, suffix, input, actionsactions 含上下按钮
Input.TextArearoot, textarea, count
Selectroot, selector, selectionItem, arrow, clear
Modalroot, header, body, footer, close
Drawerroot, header, body, footer
Tableroot, header, body, summary, row, cell

3.3 项目中 styles 的使用

项目在多个地方使用了 Semantic DOM 的 styles 属性:

// MobileDrawerMenu.tsx - 自定义 Drawer 子区域样式
<Drawer
  styles={{
    section: { width: 280 },
    header: {
      borderBottom: '1px solid ' + themeConfig.colorBorder,
      background: themeConfig.siderBg,
      color: themeConfig.siderColor,
    },
    body: { padding: 0, background: themeConfig.siderBg },
  }}
/>

// XinTable/index.tsx - 自定义 Modal 标题区域间距
<XinForm
  modalProps={{
    styles: { header: { marginBottom: 16 } },
  }}
/>

// SettingDrawer.tsx - 调整 Drawer body 顶部间距
<Drawer styles={{ body: { paddingTop: 10 } }} />

3.4 classNames 与 Tailwind 结合

classNames 可以与 Tailwind CSS 无缝集成,快速覆盖组件样式:

<Button
  classNames={{
    root: 'bg-primary rounded-lg hover:opacity-90',
    icon: 'text-white/90',
    content: 'font-semibold',
  }}
  icon={<PlusOutlined />}
>
  创建
</Button>

<Input
  classNames={{
    root: 'shadow-sm hover:shadow-md transition-shadow',
    input: 'font-mono text-sm',
  }}
/>

<Modal
  classNames={{
    body: 'px-8 py-6',
    footer: 'border-t border-gray-100',
  }}
/>

四、样式覆盖策略

XinAdmin 项目中使用三种样式覆盖方式,适用不同场景:

方式适用场景优先级
ConfigProvider theme全局品牌色、圆角、控件高度等 token 级别最低(默认值变更)
Semantic DOM styles特定组件的子元素样式,如 Drawer 顶栏背景色中(动态主题值)
Semantic DOM classNames + Tailwind快速布局/间距/颜色覆盖高(utilities 层优先)
inline style 属性动态计算的主题色值最高(内联样式最优先)

推荐覆盖顺序

  1. 全局主题 → 使用 ConfigProvider theme 配置 token
  2. 组件默认风格 → 使用 ConfigProvider components 配置组件级 token
  3. 单个组件定制 → 使用 classNames + Tailwind 工具类
  4. 动态主题色 → 使用 stylesuseToken + inline style

全局样式覆盖示例

/* 如需覆盖 antd 组件库的全局默认样式 */
@layer components {
  .ant-table-thead > tr > th {
    font-weight: 600;
    text-transform: uppercase;
    font-size: 12px;
    letter-spacing: 0.05em;
  }
}

五、自定义 Loading 样式

项目通过经典 CSS 文件覆盖了 Ant Design Spin 组件的动画样式:

/* components/Loading/index.css */
.ant-spin-dot-item {
  background: var(--ant-color-primary);  /* 跟随主题色 */
}

.ant-spin-spinning {
  /* 自定义旋转动画 */
}

使用了 Ant Design 的 CSS 变量系统 --ant-color-primary 确保 loading 指示器跟随主题色变化。


六、参照文档