react-admin 应用主题(AppTheme)完整指南:从自定义主题、暗黑模式到 5 套内置主题
react-admin 应用主题AppTheme完整指南从自定义主题、暗黑模式到 5 套内置主题【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin本篇技术指南以 react-admin 官方文档 docs/AppTheme.md 为核心骨架系统讲解如何通过 Material UI 的主题Theming机制为整个应用定制样式——包括全局主题的设置、亮色/暗色双主题支持、5 套内置主题Default / BW / Nano / Radiant / House的选用、useTheme编程式切换主题、单个组件的样式覆写、默认属性覆写、侧边栏宽度定制以及从零编写一套符合企业品牌的自定义主题。读完本文你将能够独立为 react-admin 应用搭建开箱即用 可深度定制的完整主题体系。说明本文所有示例均来自当前仓库文中引用的源码相对路径如 packages/ra-ui-materialui/src/theme/defaultTheme.ts可在仓库中直接查阅验证。1. 应用主题机制概览react-admin 建立在 Material UI 之上因此它完整继承了 Material UI 的 Theming 能力通过一个主题对象Theme集中定义颜色palette、字体typography、间距spacing、圆角shape、阴影shadows、断点breakpoints以及每个组件的默认属性与样式覆写。主题对象可以贯穿整个应用让所有页面、表单、表格、按钮保持统一的视觉风格。在 react-admin 中主题的最终消费链路位于 packages/ra-ui-materialui/src/theme/ThemeProvider.tsxAdmin组件通过 packages/ra-ui-materialui/src/AdminContext.tsx 将theme或lightTheme与darkTheme注入ThemesContextThemeProvider读取useMediaQuery((prefers-color-scheme: dark))感知用户系统偏好结合useTheme存储的用户选择决定当前使用亮色还是暗色主题最后调用 Material UI 的createTheme()生成最终的 MUI 主题对象并交给MuiThemeProvider。这意味着你不需要手动调用 Material UI 的createTheme——react-admin 会在内部替你完成见 ThemeProvider.tsx你只需传入符合ThemeOptions形状的普通对象即可。2. 设置全局应用主题要对整个应用覆盖样式只需把一个自定义theme对象传给Admin组件即可import { Admin, defaultTheme } from react-admin; import { deepmerge } from mui/utils; import indigo from mui/material/colors/indigo; import pink from mui/material/colors/pink; import red from mui/material/colors/red; const myTheme deepmerge(defaultTheme, { palette: { primary: indigo, secondary: pink, error: red, contrastThreshold: 3, tonalOffset: 0.2, }, typography: { // 使用系统字体替代默认的 Roboto 字体 fontFamily: [-apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif].join(,), }, }); const App () ( Admin theme{myTheme} // ... /Admin );几个关键要点务必基于defaultTheme做deepmergereact-admin 的defaultTheme里包含了让整个框架正常工作的关键设定详见下文默认主题一节直接丢弃它写全新主题会导致应用回退到裸的 Material UI 默认样式最典型的表现是文本框变成outlined风格、表格间距错乱deepmerge来自mui/utils它会做深合并因此你可以只覆写需要变化的键其余自动继承颜色对象可以引用 Material UI 内置的调色板如indigo、pink也可以使用任意自定义对象可以通过theme或darkTheme选择内置主题也可以完全自写主题见下文各节。3. 亮色与暗色主题Light And Dark Themes同时支持亮色与暗色主题并允许用户在两者之间切换是管理后台的常见需求。react-admin 默认内置了暗色主题开箱即用默认跟随系统应用默认主题取决于用户操作系统的配色偏好——如果用户系统处于暗色模式react-admin 自动使用暗色主题否则使用亮色主题一键切换只要定义了darkThemepropToggleThemeButton组件就会自动出现在 AppBar 中用户可随时切换参考 packages/ra-ui-materialui/src/button/ToggleThemeButton.tsx其实现就是调用useTheme在dark与light之间切换并切换太阳/月亮图标。3.1 覆写暗色主题通过Admin的darkThemeprop 传入自定义暗色主题import { Admin, defaultDarkTheme, defaultLightTheme } from react-admin; import { deepmerge } from mui/utils; const lightTheme defaultLightTheme; const darkTheme deepmerge(defaultDarkTheme, { palette: { mode: dark } }); const App () ( Admin dataProvider{...} theme{lightTheme} darkTheme{darkTheme} // ... /Admin );3.2 完全禁用暗色主题如果应用不需要暗色主题把darkTheme设为null即可const App () ( Admin darkTheme{null} // ... /Admin );从 AdminContext.tsx 的源码可以看到该逻辑当同时传了theme但没传darkTheme时darkTheme会被置为undefined不提供暗色主题而显式传darkTheme{null}时也会被识别为不启用暗色主题。同时useTheme在darkTheme为null时会强制返回light见 packages/ra-ui-materialui/src/theme/useTheme.ts确保即使本地存储里残留了dark值也不会生效。3.3 固定默认主题忽略系统偏好如果你希望无论用户系统偏好如何都固定默认使用亮色或暗色可以设置Admin的defaultThemeprop取值为light或dark类型定义见 AdminContext.tsxconst App () ( Admin defaultThemelight // ... /Admin );defaultTheme只在用户尚未显式选择时生效在 ThemeProvider.tsx 中主题的初始值优先级为defaultThemeprop 系统偏好暗色且存在 darkTheme 时选 dark 默认light。用户一旦通过ToggleThemeButton或useTheme显式切换选择会被持久化到 store 中useTheme底层基于useStore(theme, ...)见 useTheme.ts后续访问都会沿用用户的选择。4. 内置主题Built-In Themesreact-admin 自带 5 套内置主题每套都包含亮色与暗色两个变体既可以直接使用也可以作为自定义主题的起点。它们的源码分别位于 packages/ra-ui-materialui/src/theme/ 目录下的defaultTheme.ts、bwTheme.ts、nanoTheme.ts、radiantTheme.ts、houseTheme.ts并从该目录的 index.ts 统一导出。主题亮色暗色定位DefaultdefaultLightTheme即defaultThemedefaultDarkTheme通用桌面与移动端皆宜BWbwLightThemebwDarkTheme高对比黑白适合视障用户与桌面应用NanonanoLightThemenanoDarkTheme高密度极简适合复杂桌面应用RadiantradiantLightThemeradiantDarkTheme强调清晰易用、酸系配色HousehouseLightThemehouseDarkTheme年轻活泼、圆角明亮4.1 Default默认主题默认主题适配性最好桌面与移动端表现同样出色且完全无需任何配置——它是 react-admin 的开箱即用主题defaultTheme defaultLightTheme。其核心内容定义在 packages/ra-ui-materialui/src/theme/defaultTheme.ts亮色变体背景色#fafafbsecondary 主色#2196f3Material Blue并对MuiFilledInput的禁用背景做了微调暗色变体mode: darkprimary 主色#90caf9背景色#313131两者共享一套defaultThemeInvariants基础设定包括typography.h6.fontWeight: 400标题不再默认加粗sidebar: { width: 240, closedWidth: 50 }注意本文档给出的默认closedWidth为 55实际仓库源码 defaultTheme.ts 中为 50应以仓库源码为准MuiTextField/MuiFormControl默认variant: filled、margin: dense、size: small、fullWidth: true这正是 react-admin 表单输入框填充式 紧凑 通栏观感的来源MuiAutocomplete默认fullWidth并在小屏断点下强制 100% 宽度MuiTableCell、MuiListItemIcon等表格/菜单相关的间距修正RaSimpleFormIterator、RaTranslatableInputs默认fullWidth: true。4.2 BW黑白主题高对比度黑白主题专为视障用户设计风格类似 shadcn现代感强适合桌面应用。其实现见 packages/ra-ui-materialui/src/theme/bwTheme.ts去除了所有阴影shadows全部为none、禁用按钮/图标按钮的涟漪动画disableRipple、默认按钮为outlined、使用 18px 的htmlFontSize与 14px 的fontSize获得清晰可读的大字号。import { Admin, bwLightTheme, bwDarkTheme } from react-admin; export const App () ( Admin dataProvider{dataProvider} theme{bwLightTheme} darkTheme{bwDarkTheme} // ... /Admin );BW 主题使用 Geist 字体需要在index.html中引入link hrefhttps://fonts.googleapis.com/css2?familyGeist:wght100..900displayswap relstylesheet4.3 Nano紧凑主题高密度、极简框架适合功能复杂的桌面应用更小的字号fontSize: 12、收紧的间距spacing: 8且MuiTableCell、MuiTab、MuiToolbar等都被压缩、文本按钮MuiButton默认variant: text、标准变体输入框MuiTextField默认variant: standard、淡雅配色、无圆角borderRadius: 0。源码见 packages/ra-ui-materialui/src/theme/nanoTheme.ts仅适合桌面端。import { Admin, nanoLightTheme, nanoDarkTheme } from react-admin; import { dataProvider } from ./dataProvider; export const App () ( Admin dataProvider{dataProvider} theme{nanoLightTheme} darkTheme{nanoDarkTheme} // ... /Admin );Nano 主题使用 Inter 字体需要在index.html中引入link hrefhttps://fonts.googleapis.com/css2?familyInter:wght300;400;500;700displayswap relstylesheet4.4 Radiant明亮主题强调清晰与易用宽松的边距spacing: 10、描边输入框与按钮variant: outlined、取消大写转换textTransform: undefined、酸性配色紫罗兰主色#9055fd、荧光粉、青柠黄告警色。源码见 packages/ra-ui-materialui/src/theme/radiantTheme.ts其中还利用alpha()基于主色生成了多层柔和阴影。import { Admin, radiantLightTheme, radiantDarkTheme } from react-admin; import { dataProvider } from ./dataProvider; export const App () ( Admin dataProvider{dataProvider} theme{radiantLightTheme} darkTheme{radiantDarkTheme} // ... /Admin );Radiant 主题使用 Gabarito 字体需要在index.html中引入link hrefhttps://fonts.googleapis.com/css2?familyGabarito:wght500;600;700;900displayswap relstylesheet4.5 House活泼主题年轻、愉悦的风格大圆角borderRadius: 20、模糊背景backdropFilter: blur(2px)、大内边距、明亮配色亮色为深蓝紫#344767主色 洋红#f90283次色。源码见 packages/ra-ui-materialui/src/theme/houseTheme.ts其 AppBar 甚至带有渐变背景linear-gradient(310deg, primary.light, secondary.main)。import { Admin, houseLightTheme, houseDarkTheme } from react-admin; import { dataProvider } from ./dataProvider; export const App () ( Admin dataProvider{dataProvider} theme{houseLightTheme} darkTheme{houseDarkTheme} // ... /Admin );House 主题使用 Open Sans 字体需要在index.html中引入link hrefhttps://fonts.googleapis.com/css2?familyOpenSans:wght300;500;600;700displayswap relstylesheet注意BW、Nano、Radiant、House 四套主题均绑定了专用字体使用它们时必须按上述方式在index.html中引入对应字体链接否则界面会回退到后备字体、观感大打折扣。5. 编程式切换主题useThemereact-admin 提供useThemeHook 用于在代码中读取与更新主题模式其用法与useState一致ToggleThemeButton内部也正是基于它实现的见 useTheme.ts。import { defaultTheme, useTheme } from react-admin; import { Button } from mui/material; const ThemeToggler () { const [theme, setTheme] useTheme(); return ( Button onClick{() setTheme(theme dark ? light : dark)} {theme dark ? Switch to light theme : Switch to dark theme} /Button ); }实现细节useTheme.ts返回值类型为[ThemeType, ThemeSetter]ThemeType即light | dark底层通过useStore(theme, ...)持久化用户选择store 默认落盘到 localStorage因此用户刷新页面后主题选择依然保留未传初始值时会依据系统偏好与是否配置了darkTheme推断默认值若当前darkTheme为null未启用暗色主题即使 store 中残留dark返回值也会被强制修正为light。useTheme也支持传入默认值const [theme, setTheme] useTheme(light)。6. 定制单个组件的全局样式在自定义主题中可以通过components键为整个应用内的某个组件覆写样式。例如定制DataTable的全局样式import { defaultTheme } from react-admin; import { deepmerge } from mui/utils; const theme deepmerge(defaultTheme, { components: { RaDataTable: { styleOverrides: { root: { backgroundColor: Lavender, .RaDataTable-headerCell: { backgroundColor: MistyRose, }, } } } } }); const App () ( Admin theme{theme} // ... /Admin );这里有两点必须注意也是新手最容易踩的坑必须与defaultTheme深合并不要直接写一个新的components对象覆盖默认主题否则会丢掉 react-admin 对 MUI 组件尤其是文本输入框filled变体的默认设定自定义覆写必须放在root键下styleOverrides.root内部使用的样式语法与sxprop 完全一致支持 .RaDataTable-headerCell这类后代选择器来精确定位组件内部的子元素类名。如何得知组件内部子元素的类名如上面的.RaDataTable-headerCell两个途径用浏览器开发者工具DevTools直接检查渲染出的 DOM查阅 react-admin 官方文档中各组件的 CSS API 说明例如DataTable的 sx 与 CSS 文档文档会列出每个可覆写的根类名与子类名。替代方案除了主题覆写你也可以重新导出自定义样式的组件styled components在 JSX 中直接使用它们替换原组件。参考 Reusable Components 文档中的示例。7. 覆写组件的默认属性Default Props同一套技术不仅能覆写样式还能覆写组件的默认属性defaultProps。react-admin 自己就是这么做的——它通过defaultTheme给所有TextField应用了filled变体。例如想全局把输入框改成outlined变体import { defaultTheme } from react-admin; import { deepmerge } from mui/utils; const theme deepmerge(defaultTheme, { components: { MuiTextField: { defaultProps: { variant: outlined, }, }, MuiFormControl: { defaultProps: { variant: outlined, }, }, } });TypeScript 提示覆写variant这类联合类型属性时TypeScript 会较真地报类型错误。解决办法是把值用as const断言为字面量类型import { defaultTheme } from react-admin; import { deepmerge } from mui/utils; const theme deepmerge(defaultTheme, { components: { MuiTextField: { defaultProps: { variant: outlined as const, }, }, MuiFormControl: { defaultProps: { variant: outlined as const, }, }, } });这套方法同样适用于 react-admin 自己的组件。例如为所有SaveButton统一更换图标import { defaultTheme } from react-admin; import { deepmerge } from mui/utils; import CheckIcon from mui/icons-material/Check; const theme deepmerge(defaultTheme, { components: { RaSaveButton: { defaultProps: { icon: CheckIcon /, }, }, } });从 defaultTheme.ts 的源码可以看到 react-admin 大量使用了这一机制MuiTextField/MuiFormControl的filled dense small fullWidth、MuiAutocomplete的fullWidth、RaSimpleFormIterator与RaTranslatableInputs的fullWidth等都是通过defaultProps在主题层统一注入的。你完全可以用同样的方式批量约束你自己的组件行为。8. 自定义侧边栏宽度侧边栏Sidebar的宽度可以通过自定义主题中的sidebar键直接调整import { defaultTheme } from react-admin; import { deepmerge } from mui/utils; const theme deepmerge(defaultTheme, { sidebar: { width: 300, // 默认值 240 closedWidth: 70, // 默认值 55仓库源码 defaultTheme.ts 中为 50 }, }); const App () ( Admin theme{theme} dataProvider{...} // ... /Admin );底层实现中Sidebar组件正是通过lodashGet(theme, sidebar.width, DRAWER_WIDTH)与lodashGet(theme, sidebar.closedWidth, CLOSED_DRAWER_WIDTH)读取这些值并控制抽屉宽度见 packages/ra-ui-materialui/src/layout/Sidebar.tsx因此自定义主题里的sidebar.width/sidebar.closedWidth会直接作用于应用侧边栏。默认值提示本文档示例注释中closedWidth写的是 55但当前仓库源码 defaultTheme.ts 中为50实际生效值以源码为准。如果需要更深度的侧边栏定制如背景色、宽度过渡动画等可以传入自定义Sidebar组件给Layoutimport { Sidebar, Layout } from react-admin; const MySidebar (props) ( Sidebar sx{{ .RaSidebar-drawerPaper: { backgroundColor: red, }, }} {...props} / ); const MyLayout ({ children }) ( Layout sidebar{MySidebar} {children} /Layout );9. 从零编写自定义主题Material UI 的主题系统允许你完全重写默认调色板、排版、颜色等非常适用于让 react-admin 贴合公司品牌形象。一个theme对象可以包含以下顶层键breakpoints— 响应式断点direction— 文本方向ltr/rtlmixins— 常用混入样式components— 各组件默认属性与样式覆写palette— 调色板primary / secondary / error / warning / info / success / background / text 等props— 全局默认 props旧版 API现推荐使用components.MuiXxx.defaultPropsshadows— 阴影数组spacing— 间距基数8 的倍数体系transitions— 过渡动画typography— 字体、字号、字重体系zIndex— 层级管理Material UI 默认主题中这些键的默认值与含义可参考官方默认主题文档mui/material的默认主题说明。下面是一个从零书写的品牌主题示例演示了如何定义主/次色、间距、字体并通过MuiCssBaseline注入自定义font-faceimport { lime, purple } from mui/material/colors; import type { ThemeOptions } from mui/material; const theme: ThemeOptions { palette: { primary: { main: #FF5733, // light: 会根据 palette.primary.main 自动计算 // dark: 会根据 palette.primary.main 自动计算 // contrastText: 会根据与 palette.primary.main 的对比度自动计算 }, secondary: { main: #E0C2FF, light: #F5EBFF, // dark: 会根据 palette.secondary.main 自动计算 contrastText: #47008F, }, }, spacing: 4, typography: { fontFamily: Raleway, Arial, }, components: { MuiCssBaseline: { styleOverrides: font-face { font-family: Raleway; font-style: normal; font-display: swap; font-weight: 400; src: local(Raleway), local(Raleway-Regular), url(${RalewayWoff2}) format(woff2); unicodeRange: U0000-00FF, U0131, U0152-0153, U02BB-02BC, U02C6, U02DA, U02DC, U2000-206F, U2074, U20AC, U2122, U2191, U2193, U2212, U2215, UFEFF; } , }, }, };定义好主题后通过themeprop 传给Admin即可const App () ( Admin theme{myTheme} dataProvider{...} // ... /Admin );写自定义主题有两种路径完全从零书写如上例完整控制每个键以defaultTheme为起点用deepmerge覆盖局部值——这是官方推荐的做法风险最低import { deepmerge } from mui/utils; import { defaultTheme } from react-admin; const theme deepmerge(defaultTheme, { components: { RaDataTable: { styleOverrides: { root: { backgroundColor: Lavender, .RaDataTable-headerCell: { backgroundColor: MistyRose, }, } } } } });10. 深入react-admin 默认主题全貌packages/ra-ui-materialui/src/theme/defaultTheme.ts 中导出了三个主题对象它们的关系如下defaultThemeInvariants亮暗共享的不变量——标题字重、sidebar尺寸、以及 MUI / ra 组件的默认 propsdefaultLightTheme在不变量的基础上叠加亮色调色板背景#fafafb、secondary 主色#2196f3与MuiFilledInput/RaToolbar的样式微调defaultDarkTheme在不变量的基础上叠加暗色调色板mode: dark、primary 主色#90caf9、背景#313131与RaToolbar暗色背景defaultTheme defaultLightThemedefaultTheme就是亮色主题的别名。完整默认主题结构如下取自仓库源码保证与当前版本一致import type { ThemeOptions } from mui/material; import { deepmerge } from mui/utils; const defaultThemeInvariants: ThemeOptions { typography: { h6: { fontWeight: 400, }, }, sidebar: { width: 240, closedWidth: 50, }, components: { MuiAutocomplete: { defaultProps: { fullWidth: true, }, variants: [ { props: {}, style: ({ theme }) ({ [theme.breakpoints.down(sm)]: { width: 100% }, }), }, ], }, MuiTextField: { defaultProps: { variant: filled as const, margin: dense as const, size: small as const, fullWidth: true, }, variants: [ { props: {}, style: ({ theme }) ({ [theme.breakpoints.down(sm)]: { width: 100% }, }), }, ], }, MuiFormControl: { defaultProps: { variant: filled as const, margin: dense as const, size: small as const, fullWidth: true, }, }, RaSimpleFormIterator: { defaultProps: { fullWidth: true, }, }, RaTranslatableInputs: { defaultProps: { fullWidth: true }, }, }, }; export const defaultLightTheme: ThemeOptions deepmerge( defaultThemeInvariants, { palette: { background: { default: #fafafb, }, secondary: { light: #6ec6ff, main: #2196f3, dark: #0069c0, contrastText: #fff, }, }, components: { MuiFilledInput: { styleOverrides: { root: { backgroundColor: rgba(0, 0, 0, 0.04), $disabled: { backgroundColor: rgba(0, 0, 0, 0.04), }, }, }, }, }, } ); export const defaultDarkTheme: ThemeOptions deepmerge( defaultThemeInvariants, { palette: { mode: dark, primary: { main: #90caf9, }, background: { default: #313131, }, }, } ); export const defaultTheme defaultLightTheme;以上代码与仓库当前源码 defaultTheme.ts 略有出入仓库还包含MuiTableCell、MuiListItemIcon、RaToolbar等更多覆写展示的是文档视角下的核心结构。实际开发中建议直接import { defaultTheme } from react-admin后基于deepmerge定制无需手抄这份配置。11. 主题优先级与持久化小结综合源码可以把 react-admin 的主题决策流程归纳为初始主题选择defaultThemeproplight | dark 系统prefers-color-scheme: dark且已配置darkThemelight用户显式切换ToggleThemeButton或useTheme调用setTheme结果通过useStore(theme)持久化localStorage刷新不丢失渲染ThemeProvider依据最终mode从ThemesContext中选择lightTheme默认defaultLightTheme可被themeprop 覆盖或darkTheme调用createTheme()生成 MUI 主题暗色主题禁用darkTheme{null}时强制使用亮色主题store 中的dark残留值不生效。相关源码入口主题注入Admin theme | darkTheme | defaultTheme→ packages/ra-ui-materialui/src/AdminContext.tsx主题生成与系统偏好ThemeProvider→ packages/ra-ui-materialui/src/theme/ThemeProvider.tsx读写与持久化useTheme→ packages/ra-ui-materialui/src/theme/useTheme.ts内置主题导出defaultTheme/bwLightTheme/nanoLightTheme/radiantLightTheme/houseLightTheme及对应暗色变体 → packages/ra-ui-materialui/src/theme/index.ts侧边栏尺寸读取Sidebar→ packages/ra-ui-materialui/src/layout/Sidebar.tsx一键切换按钮ToggleThemeButton→ packages/ra-ui-materialui/src/button/ToggleThemeButton.tsx12. 相关文档与延伸阅读Admin.md —Admin组件的theme/darkTheme/defaultThemepropsToggleThemeButton.md — AppBar 中的主题切换按钮SX.md —sxprop 样式语法与可复用组件DataTable.md —DataTable的 CSS API查找可覆写的类名Theming.md — 主题与视觉风格总览内置主题源码packages/ra-ui-materialui/src/theme/defaultTheme.ts、bwTheme.ts、nanoTheme.ts、radiantTheme.ts、houseTheme.ts【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →