Refine v5 定制 Chakra UI 主题:从 RefineThemes 预设到自定义品牌色实战
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
在 Refine 中,UI 层(Chakra UI、Ant Design、MUI、Mantine 等)与核心逻辑完全解耦,因此你可以像使用任何原生 Chakra UI 应用一样,自由定制设计与主题。本篇文章以仓库中的customization-theme-chakra-ui示例为主线,讲解如何在 Refine v5 中接入RefineThemes预设主题、通过extendTheme定制品牌色、以及实现暗色模式切换。读完本文,你将掌握 Refine + Chakra UI 项目主题定制的完整路径,并能对照@refinedev/chakra-ui的源码理解其主题实现原理。
一、主题定制在 Refine 中的整体思路
Refine 本身是一个 headless 的 React 框架,负责数据获取、认证、路由、表单状态等核心能力,而 UI 组件由@refinedev/chakra-ui这类集成包提供。这意味着:
- 主题定制发生在UI 集成包层面,核心
@refinedev/core不参与任何样式决策; @refinedev/chakra-ui导出的RefineThemes、ThemedLayout、ErrorComponent等组件,全部基于 Chakra UI 的ChakraProvider上下文工作;- 你可以在不改动任何业务逻辑的前提下,通过一个
theme对象改变整个后台界面的观感。
示例文档的定位正是如此:它声明"你可以通过 Refine 在项目中定制设计与主题",并在customization-theme-chakra-ui示例中演示了定制默认主题后的完整 CRUD 应用。该示例的全部源码位于 examples/customization-theme-chakra-ui。
二、快速运行示例
示例的 README.md 给出了两种运行方式:本地创建与 CodeSandbox 在线预览。在本地终端执行以下命令即可基于该示例初始化一个完整的 Refine 应用:
npm create refine-app@latest -- --example customization-theme-chakra-ui初始化完成后,进入项目目录并启动开发服务器:
npm install npm run dev从 package.json 可以看到该示例的核心技术栈与版本前提(Node 需满足>=20):
| 依赖 | 版本 | 作用 |
|---|---|---|
@refinedev/core | ^5.0.12 | Refine 核心(数据、认证、路由) |
@refinedev/chakra-ui | ^3.0.2 | Chakra UI 集成包(主题、布局、CRUD 组件) |
@chakra-ui/react | ^2.5.1 | Chakra UI 底层组件库 |
@refinedev/simple-rest | ^6.0.1 | REST 数据提供器 |
@refinedev/react-table | ^6.0.1 | TanStack Table 集成 |
@refinedev/react-hook-form | ^5.0.4 | React Hook Form 集成 |
@refinedev/react-router | ^2.0.4 | React Router v7 路由集成 |
react/react-router | ^19.1.0 / ^7.0.2 | React 与路由运行时 |
三、入口集成:用 ChakraProvider 挂载 RefineThemes
主题定制的第一步发生在应用的根组件。查看示例的 src/App.tsx,核心代码只有一处:用ChakraProvider包裹整个<Refine>,并把RefineThemes.Blue作为主题传入:
import { GitHubBanner, Refine } from "@refinedev/core"; import { ErrorComponent, ThemedLayout, useNotificationProvider, RefineThemes, } from "@refinedev/chakra-ui"; import { ChakraProvider } from "@chakra-ui/react"; import dataProvider from "@refinedev/simple-rest"; import routerProvider, { NavigateToResource, UnsavedChangesNotifier, DocumentTitleHandler, } from "@refinedev/react-router"; import { BrowserRouter, Routes, Route, Outlet } from "react-router"; import { PostList, PostCreate, PostEdit, PostShow } from "./pages"; import { Header } from "./components/header"; const App: React.FC = () => { return ( <BrowserRouter> <GitHubBanner /> <ChakraProvider theme={RefineThemes.Blue}> <Refine routerProvider={routerProvider} dataProvider={dataProvider("https://api.fake-rest.refine.dev")} notificationProvider={useNotificationProvider()} resources={[ { name: "posts", list: "/posts", show: "/posts/show/:id", create: "/posts/create", edit: "/posts/edit/:id", }, ]} options={{ syncWithLocation: true, warnWhenUnsavedChanges: true, }} > <Routes> <Route element={ <ThemedLayout Header={Header}> <Outlet /> </ThemedLayout> } > <Route index element={<NavigateToResource resource="posts" />} /> <Route path="/posts"> <Route index element={<PostList />} /> <Route path="create" element={<PostCreate />} /> <Route path="edit/:id" element={<PostEdit />} /> <Route path="show/:id" element={<PostShow />} /> </Route> <Route path="*" element={<ErrorComponent />} /> </Route> </Routes> <UnsavedChangesNotifier /> <DocumentTitleHandler /> </Refine> </ChakraProvider> </BrowserRouter> ); }; export default App;这段代码同时体现了 Refine v5 的几个标准接线:
routerProvider负责路由解析与导航(由@refinedev/react-router提供);dataProvider指向演示用的 REST 接口https://api.fake-rest.refine.dev;ThemedLayout提供带侧边栏、头部、内容区的标准后台布局,并把自定义Header注入其中;syncWithLocation让查询状态与 URL 同步,warnWhenUnsavedChanges配合UnsavedChangesNotifier提示未保存的更改。
四、RefineThemes 源码剖析:七种预设主题是如何生成的
示例中只写了一行theme={RefineThemes.Blue},但RefineThemes并非魔法常量。它定义在 packages/chakra-ui/src/theme/index.ts,其实现揭示了 Refine 主题体系的设计:
首先,内置七种预设色板,分别映射到 Chakra UI 基础主题的不同颜色:
const refineCustomThemes = { Blue: baseTheme.colors.blue, Purple: baseTheme.colors.purple, Magenta: baseTheme.colors.pink, Red: baseTheme.colors.red, Orange: baseTheme.colors.orange, Yellow: baseTheme.colors.yellow, Green: baseTheme.colors.green, };然后通过Object.keys(...).reduce为每个色板调用一次extendTheme,批量生成RefineThemes.Blue、RefineThemes.Purple等七个主题对象(见 theme/index.ts#L92-L124):
export const RefineThemes = Object.keys(refineCustomThemes).reduce( (acc, key) => { const themeName = key as keyof typeof refineCustomThemes; return { ...acc, [key]: extendTheme({ config: { initialColorMode: "system", }, styles: { global: (props: StyleFunctionProps) => { const bgLight = props.theme.colors.gray[50]; const bgDark = props.theme.colors.gray[900]; return { "html, body": { background: mode(bgLight, bgDark)(props), }, }; }, }, colors: { brand: refineCustomThemes[themeName], refine: { ...refineCustomColors, }, }, }), }; }, {}, ) as Record<keyof typeof refineCustomThemes, RefineTheme>;每个预设主题都做了三件事:
config.initialColorMode: "system":跟随操作系统偏好决定初始明暗模式;- 全局背景色:通过
@chakra-ui/theme-tools的mode()函数,在浅色模式下使用gray.50、深色模式下使用gray.900作为html, body的背景,实现明暗模式的全局响应; - 注入
brand与refine两组自定义颜色:brand即所选主色板,refine则定义布局组件的专用色彩。
其中refine色板的结构在源码中定义如下:
const refineCustomColors = { header: { bg: { light: baseTheme.colors.white, dark: baseTheme.colors.gray[800], }, }, sider: { bg: { light: baseTheme.colors.white, dark: baseTheme.colors.gray[800], }, header: { light: baseTheme.colors.white, dark: baseTheme.colors.gray[800], }, }, } as const;也就是说,ThemedLayout中的头部(header)与侧边栏(sider)背景色会依据明暗模式在白色与gray.800之间切换,这是 Refine 预设主题实现暗色模式适配的关键。
五、自定义品牌色:参考默认主题写出自己的 theme
当七种预设色板不够用时,你可以完全自定义。源码中导出的默认主题refineTheme(见 theme/index.ts#L9-L43)就是一个最佳范本,它展示了如何通过 Chakra UI 的extendTheme一次性定制颜色、字体与全局样式:
import { extendTheme, baseTheme } from "@chakra-ui/react"; export const refineTheme = extendTheme({ config: { initialColorMode: "system", }, fonts: { heading: "Montserrat, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", body: "Montserrat, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'", }, styles: { global: () => ({ "html, body": { fontSize: "14px", }, }), }, colors: { brand: { 50: "#E6FFFA", 100: "#B2F5EA", 200: "#81E6D9", 300: "#4FD1C5", 400: "#38B2AC", 500: "#319795", 600: "#2C7A7B", 700: "#285E61", 800: "#234E52", 900: "#1D4044", }, sider: { background: "#2A132E", collapseButton: "#150A17", }, }, });从中可以看到自定义主题的两个核心要点:
brand色阶必须提供完整的 50~900 十档色值,因为RefineTheme接口要求brand具备这十个档位(见 theme/index.ts#L74-L90),Refine 的按钮、链接、高亮等组件都会按档位取色;- 字体与全局样式同样在此处统一管理,例如将全局字号收敛为 14px,更适合管理后台的密集信息展示。
自定义时,你只需在项目中新建一个theme.ts,写出类似上面的对象,再替换入口处的主题即可:
import { ChakraProvider, extendTheme } from "@chakra-ui/react"; const customTheme = extendTheme({ colors: { brand: { /* 50 到 900 的十档色值 */ }, }, }); <ChakraProvider theme={customTheme}> <Refine>...</Refine> </ChakraProvider>六、暗色模式切换:Header 中的 colorMode 控制
主题定制不止于初始色板。示例自定义的 Header 组件 演示了如何在应用内动态切换明暗模式:
import { HamburgerMenu } from "@refinedev/chakra-ui"; import { Box, IconButton, Icon, useColorMode } from "@chakra-ui/react"; import { IconMoon, IconSun } from "@tabler/icons-react"; export const Header = () => { const { colorMode, toggleColorMode } = useColorMode(); return ( <Box py="2" px="4" display="flex" justifyContent="space-between" alignItems="center" w="full" bg="chakra-body-bg" height="64px" position="sticky" top="0" zIndex="1" > <HamburgerMenu /> <IconButton variant="ghost" aria-label="Toggle theme" onClick={toggleColorMode} > <Icon as={colorMode === "light" ? IconMoon : IconSun} w="18px" h="18px" /> </IconButton> </Box> ); };关键点:
useColorMode()与toggleColorMode()来自@chakra-ui/react,与 Refine 无耦合,任何 Chakra UI 应用都能直接使用;- 由于预设主题的
initialColorMode为"system",切换只影响当前会话的显示模式,不会破坏"跟随系统"的初始行为; HamburgerMenu由@refinedev/chakra-ui导出,用于控制ThemedLayout侧边栏的折叠展开,它与主题色板(sider色组)协同工作。
七、主题下的页面组件:列表、筛选与表单如何响应主题
换肤的效果最终要落到页面组件上。示例以posts资源为例实现了完整的 list / create / edit / show 四个页面,它们全部复用@refinedev/chakra-ui导出的List、Create、Edit、Show以及ShowButton、EditButton、DeleteButton、DateField等组件,因此只需切换主题对象,整套 CRUD 界面就会随之变色。
以列表页 src/pages/posts/list.tsx 为例,它演示了三个值得关注的点:
useTable来自@refinedev/react-table,返回的reactTable对象直接对接 TanStack Table,配合 Chakra 的Table、Thead、Th等组件渲染;- 列级筛选与排序由自定义的 ColumnFilter 和 ColumnSorter 组件实现,其中
status列的meta.filterElement直接渲染了一个 ChakraSelect,下拉选项(published / draft / rejected)会继承当前主题的表单样式; - 关联数据通过
useMany加载,分类名称从categories资源批量获取后,通过table.options.meta注入表格上下文,再在category.id列的cell中映射展示。
而表单页(如 src/pages/posts/create.tsx)使用@refinedev/react-hook-form的useForm,表单控件(FormControl、Input、Select、FormErrorMessage)全部来自 Chakra UI,同样会自动套用当前主题的配色与焦点样式,无需任何额外配置。
八、版本与使用前提
- 本文所述内容基于当前仓库中的 Refine v5(
@refinedev/core^5.0.12)与@refinedev/chakra-ui^3.0.2,示例运行需要 Node>=20; RefineThemes的七种预设色板(Blue、Purple、Magenta、Red、Orange、Yellow、Green)定义于 packages/chakra-ui/src/theme/index.ts,其中Magenta实际映射的是 Chakra 的pink色板,从源码结构看这是刻意为之的命名映射;- 由于 Refine 将 UI 与核心解耦,本文介绍的主题定制方案仅作用于
@refinedev/chakra-ui相关组件与自定义的 Chakra 组件,不会影响 headless 层的数据与状态逻辑——这正是"想换主题就换主题,业务代码零改动"的灵活性来源。
总而言之,Refine + Chakra UI 的主题定制路径可以浓缩为三步:在ChakraProvider挂载RefineThemes.*预设或自定义extendTheme产物 → 通过ThemedLayout与自定义Header承接布局与明暗切换 → 业务页面复用集成包的 CRUD 组件自动换肤。对照源码查看 theme/index.ts 的reduce生成逻辑,你还可以按同样的模式扩展出属于自己的品牌色预设。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考