Backstage 主题定制完全指南:基于 @backstage/theme 打造专属外观(旧前端系统)
本篇技术指南面向仍在使用旧前端系统(Old Frontend System,即通过 @backstage/app-defaults 的 createApp 组合应用)的 Backstage 应用,系统讲解如何利用官方 @backstage/theme 包定制应用主题外观:从创建自定义主题、注册到应用,到精细化的排版、字体、组件样式覆盖、Logo、图标、侧边栏子菜单定制。读完本文,你将能独立完成一套从配色、字体到图标、Logo 的完整 Backstage 品牌化改造。若你的应用已迁移到新前端系统,请阅读 新版指南。
主题系统概览:@backstage/theme 包
Backstage 内置了一套默认主题,包含浅色(light)与深色(dark)两种模式变体。这套主题由 @backstage/theme 包提供,该包还导出了大量用于定制默认主题、或从零创建全新主题的工具函数。
从仓库源码看,@backstage/theme 的核心导出集中在 packages/theme/src/index.ts,它重新导出了 unified、base、v4、v5 四个子模块,其中:
unified:createUnifiedTheme、UnifiedThemeProvider、themes(内置 light/dark)、createUnifiedThemeFromV4等统一主题(Unified Theme)相关 API;base:createBaseThemeOptions、palettes、pageTheme、shapes、genPageTheme、defaultTypography、colorVariants等基础构造工具与类型;v4/v5:分别面向 Material UI v4 与 v5 的主题适配层。
其中值得特别说明的是 Unified Theme(统一主题) 机制:createUnifiedTheme 会同时生成 Material UI v4 与 v5 两套主题对象(见 UnifiedTheme.tsx),并通过 UnifiedThemeProvider 同时为 @material-ui/*(v4)和 @mui/*(v5)组件注入对应主题,这正是 Backstage 在过渡期能同时兼容两代 Material UI 的关键。
创建自定义主题:createUnifiedTheme 起步
创建新主题最简单的方式是使用 @backstage/theme 导出的 createUnifiedTheme 函数,用它覆盖默认主题的基础参数,如调色板(color palette)与字体(font)。
例如,基于默认浅色主题创建一个新主题:
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
export const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
}),
fontFamily: 'Comic Sans MS',
defaultPageTheme: 'home',
});
建议:在
packages/app/src下创建theme文件夹来存放主题文件,让项目结构更整洁。
createUnifiedTheme 接收一个 UnifiedThemeOptions 对象(定义见 UnifiedTheme.tsx),内部先调用 createBaseThemeOptions 生成基础配置,再合并默认组件主题与你的 components 覆盖项,最终同时产出 v4、v5 两套 Material UI 主题。
createBaseThemeOptions 的默认值
理解 createBaseThemeOptions 的默认行为有助于掌握主题覆盖的边界。查看其源码 createBaseThemeOptions.ts:
htmlFontSize默认 16;fontFamily默认'"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif';defaultPageTheme默认home,且必须在pageTheme中存在,否则会抛出X is not defined in pageTheme.错误;pageTheme默认使用内置的全套页面主题;- 返回对象中包含
page(当前默认页主题)与getPageTheme({ themeId })(按主题 ID 取页面主题,未命中时回退到默认页主题)两个 Backstage 扩展字段。
内置调色板 palettes
palettes.light 与 palettes.dark 定义了 Backstage 的全部基础色板,见 palettes.ts。除了 Material UI 标准的 primary、secondary、background 等,还包含大量 Backstage 扩展配色,覆盖以下类别:
status:ok、warning、error、running、pending、aborted六种状态色;banner:横幅的info、error、text、link、closeButtonColor、warning;- 文本与背景类:
textContrast、textVerySubtle、textSubtle、highlight、errorBackground、warningBackground、infoBackground、errorText、infoText、warningText、link、linkHover、gold、border; navigation:侧边导航的background、indicator、color、selectedColor,以及可选的navItem.hoverBackground、submenu.background;tabbar:indicator;pinSidebarButton:icon、background;bursts(已标记 deprecated,未来版本会移除):fontColor、slackChannelText、backgroundColor、gradient。
如果你不需要基于默认主题微调,也可以从零构建一个符合 BackstageTheme 类型的主题——@backstage/theme 导出了完整的类型定义,关于从零构建可参考 Material UI 的 theming 文档。
将自定义主题接入应用(旧前端系统)
旧前端系统中,自定义主题通过 createApp 的 themes 配置项传入。接入上文创建的主题:
import { createApp } from '@backstage/app-defaults';
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import LightIcon from '@material-ui/icons/WbSunny';
import { UnifiedThemeProvider} from '@backstage/theme';
import { myTheme } from './themes/myTheme';
const app = createApp({
apis: ...,
plugins: ...,
themes: [{
id: 'my-theme',
title: 'My Custom Theme',
variant: 'light',
icon: <LightIcon />,
Provider: ({ children }) => (
<UnifiedThemeProvider theme={myTheme} children={children} />
),
}]
})
每个主题项包含四个关键字段:
| 字段 | 说明 |
|---|---|
id |
主题唯一标识,用于切换与存储用户选择 |
title |
在主题切换菜单中展示的名称 |
variant |
light 或 dark,决定主题模式 |
icon |
主题切换菜单中显示的图标组件 |
Provider |
包裹应用子树的主题 Provider,这里使用 UnifiedThemeProvider 注入 Unified Theme |
需要留意:你传入的自定义主题列表会覆盖默认主题。如果仍想保留默认的浅色/深色主题,可以直接从 @backstage/theme 导入它们——仓库中默认主题定义于 themes.ts:
import { themes } from '@backstage/theme';
// themes.light / themes.dark
export const themes = {
light: createUnifiedTheme({ palette: palettes.light }),
dark: createUnifiedTheme({ palette: palettes.dark }),
};
UnifiedThemeProvider 的工作原理
UnifiedThemeProvider(见 UnifiedThemeProvider.tsx)会从 Unified Theme 中分别取出 v4 与 v5 主题,并同时挂载:
- 对 v4:
StylesProvider(使用jss4-生产前缀避免与 v5 样式冲突)+ThemeProvider; - 对 v5:
StyledEngineProvider injectFirst+ MUI v5 的ThemeProvider。
同时它还通过 useApplyThemeAttributes(见 useApplyThemeAttributes.ts)在 <body> 上维护 data-theme-mode、data-theme-name、data-unified-theme-stack 三个属性,供全局 CSS 按主题模式/名称编写样式,这在多主题嵌套场景中尤其有用。
完整自定义主题示例:配色与页面主题
下面是一个更完整的示例,覆盖调色板全面定制与各页面头部(Header)的渐变与图形:
import {
createBaseThemeOptions,
createUnifiedTheme,
genPageTheme,
palettes,
shapes,
} from '@backstage/theme';
export const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: {
...palettes.light,
primary: {
main: '#343b58',
},
secondary: {
main: '#565a6e',
},
error: {
main: '#8c4351',
},
warning: {
main: '#8f5e15',
},
info: {
main: '#34548a',
},
success: {
main: '#485e30',
},
background: {
default: '#d5d6db',
paper: '#d5d6db',
},
banner: {
info: '#34548a',
error: '#8c4351',
text: '#343b58',
link: '#565a6e',
},
errorBackground: '#8c4351',
warningBackground: '#8f5e15',
infoBackground: '#343b58',
navigation: {
background: '#343b58',
indicator: '#8f5e15',
color: '#d5d6db',
selectedColor: '#ffffff',
},
},
}),
defaultPageTheme: 'home',
fontFamily: 'Comic Sans MS',
/* below drives the header colors */
pageTheme: {
home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
documentation: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave2,
}),
tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }),
service: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave,
}),
website: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave,
}),
library: genPageTheme({
colors: ['#8c4351', '#343b58'],
shape: shapes.wave,
}),
other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
},
});
genPageTheme、shapes 与 colorVariants 源码解读
pageTheme 对应各页面类型(home、documentation、tool、service、website、library、other、app、apis、card)的头部背景。相关实现位于 pageTheme.ts:
genPageTheme({ colors, shape, options })生成一个PageTheme:当colors只有一个值时自动复制为两个,拼出linear-gradient(90deg, ...),再与shape(SVG data URI 背景图)合并成最终backgroundImage;默认fontColor为#FFFFFF;shapes内置wave、wave2、round、square四种装饰形状(均为白色透明遮罩的 SVG data URI);colorVariants内置darkGrey、marineBlue、veryBlue、rubyRed、toastyOrange、purpleSky、eveningSea、teal、pinkSea、greens等预设渐变色组合,例如teal: ['#005B4B']、pinkSea: ['#C8077A', '#C2297D']。
内置 pageTheme 使用的组合如:home 使用 teal + wave、documentation 使用 pinkSea + wave2、tool 使用 purpleSky + round,可作为自定义时的参照。
更完整的、包含 Backstage 与 Material UI 组件覆盖(overrides)的主题示例,可参考官方 demo 站点应用的 Aperture 主题实现。
自定义排版(Typography)
创建自定义主题时,可以定制默认排版的各个方面。下面是一个基于简化主题的完整排版示例:
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
export const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
typography: {
htmlFontSize: 16,
fontFamily: 'Arial, sans-serif',
h1: {
fontSize: 54,
fontWeight: 700,
marginBottom: 10,
},
h2: {
fontSize: 40,
fontWeight: 700,
marginBottom: 8,
},
h3: {
fontSize: 32,
fontWeight: 700,
marginBottom: 6,
},
h4: {
fontWeight: 700,
fontSize: 28,
marginBottom: 6,
},
h5: {
fontWeight: 700,
fontSize: 24,
marginBottom: 4,
},
h6: {
fontWeight: 700,
fontSize: 20,
marginBottom: 2,
},
},
defaultPageTheme: 'home',
}),
});
BackstageTypography 类型(见 base/types.ts)要求 htmlFontSize、fontFamily 以及 h1–h6 各级标题的 fontSize、fontWeight、marginBottom 字段,各级标题还支持可选的 fontFamily 覆盖。
只覆盖部分排版设置
若只想覆盖排版中的一部分(例如只改 h1),需要基于 defaultTypography 展开,避免丢失其余标题样式。仓库中 defaultTypography 定义于 createBaseThemeOptions.ts,完整包含 htmlFontSize 与 h1–h6 的默认值:
import {
createBaseThemeOptions,
createUnifiedTheme,
defaultTypography,
palettes,
} from '@backstage/theme';
export const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
typography: {
...defaultTypography,
htmlFontSize: 16,
fontFamily: 'Roboto, sans-serif',
h1: {
fontSize: 72,
fontWeight: 700,
marginBottom: 10,
},
},
defaultPageTheme: 'home',
}),
});
注意 defaultTypography 是一个会被 createBaseThemeOptions 内部就地修改的共享对象(htmlFontSize 与 fontFamily 会被默认值直接赋值),因此推荐始终以展开(spread)方式使用它,以保证各个主题之间互不影响。
自定义字体(Custom Fonts)
添加自定义字体分三步:存放字体文件、声明 @font-face、通过 MuiCssBaseline 的 styleOverrides 挂载。
- 存放字体:建议在前端应用
src下创建assets/fonts目录,将字体文件(如.woff2)放入其中; - 声明字体:按 Material UI Typography 的
@font-face语法声明字体样式; - 挂载字体:在主题的
components.MuiCssBaseline.styleOverrides中,把字体定义加入'@font-face'数组。
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
const myCustomFont = {
fontFamily: 'My-Custom-Font',
fontStyle: 'normal',
fontDisplay: 'swap',
fontWeight: 300,
src: `
local('My-Custom-Font'),
url(${MyCustomFont}) format('woff2'),
`,
};
export const myTheme = createUnifiedTheme({
fontFamily: 'My-Custom-Font',
palette: palettes.light,
components: {
MuiCssBaseline: {
styleOverrides: {
'@font-face': [myCustomFont],
},
},
},
});
同时使用多种字体
如果想为正文与标题分别使用不同字体:将顶层 fontFamily 设为正文所需字体,再在 typography 中为各级标题覆盖 fontFamily。
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2';
const myCustomFont = {
fontFamily: 'My-Custom-Font',
fontStyle: 'normal',
fontDisplay: 'swap',
fontWeight: 300,
src: `
local('My-Custom-Font'),
url(${MyCustomFont}) format('woff2'),
`,
};
const myAwesomeFont = {
fontFamily: 'My-Awesome-Font',
fontStyle: 'normal',
fontDisplay: 'swap',
fontWeight: 300,
src: `
local('My-Awesome-Font'),
url(${myAwesomeFont}) format('woff2'),
`,
};
export const myTheme = createUnifiedTheme({
fontFamily: 'My-Custom-Font',
components: {
MuiCssBaseline: {
styleOverrides: {
'@font-face': [myCustomFont, myAwesomeFont],
},
},
},
...createBaseThemeOptions({
palette: palettes.light,
typography: {
...defaultTypography,
htmlFontSize: 16,
fontFamily: 'My-Custom-Font',
h1: {
fontSize: 72,
fontWeight: 700,
marginBottom: 10,
fontFamily: 'My-Awesome-Font',
},
},
defaultPageTheme: 'home',
}),
});
覆盖 Backstage 与 Material UI 组件样式
自定义主题本质上是为组件 CSS 规则提供取值来源。以 Backstage 某个组件为例,它的样式可能是这样写的:
const useStyles = makeStyles<BackstageTheme>(
theme => ({
header: {
padding: theme.spacing(3),
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
backgroundImage: theme.page.backgroundImage,
},
}),
{ name: 'BackstageHeader' },
);
关键点在于:padding 取自 theme.spacing,backgroundImage 取自 theme.page.backgroundImage,因此调整主题中的 spacing 或页面主题就能直接作用于这些属性;但 boxShadow 是硬编码的,没有引用任何主题值——仅靠自定义主题无法改变 box-shadow,也无法添加组件原本没有定义的 CSS 规则(如 margin)。这类情况必须通过 overrides(样式覆盖) 处理:
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
export const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
}),
fontFamily: 'Comic Sans MS',
defaultPageTheme: 'home',
components: {
BackstageHeader: {
styleOverrides: {
header: ({ theme }) => ({
width: 'auto',
margin: '20px',
boxShadow: 'none',
borderBottom: `4px solid ${theme.palette.primary.main}`,
}),
},
},
},
});
components 配置会与仓库内置的 defaultComponentThemes(见 v5/defaultComponentThemes.ts)合并,随后同时生成 v4 与 v5 的组件覆盖——这正是 createUnifiedTheme 中 const components = { ...defaultComponentThemes, ...options.components } 所做的事(见 UnifiedTheme.tsx)。
自定义 Logo
除了主题,你还可以定制站点左上角的 Logo。在旧前端系统中,找到应用内的 src/components/Root/ 目录,其中有两个组件:
LogoFull.tsx—— 侧边栏展开时使用的大 Logo;LogoIcon.tsx—— 侧边栏收起时使用的小 Logo。
替换方式有两种:
方式一:直接替换 SVG 定义。将上述组件中的相关代码替换为你的原始 SVG 定义。
方式二:导入位图(如 PNG)。将新图片放入子目录,例如 src/components/Root/logo/my-company-logo.png,然后:
import MyCustomLogoFull from './logo/my-company-logo.png';
const LogoFull = () => {
return <img src={MyCustomLogoFull} />;
};
图标定制
前面已经了解了主题与 Logo 的定制,接下来看如何覆盖现有图标、以及如何新增图标。
覆盖默认图标(Custom Icons)
你可以定制应用的默认图标。可覆盖的默认图标集合定义在 packages/app-defaults/src/defaults/icons.tsx,包括 catalog、scaffolder、techdocs、search、github、group、user、warning、star、unstarred、externalLink,以及 kind:api、kind:component、kind:domain、kind:group、kind:location、kind:system、kind:user、kind:resource、kind:template 等目录实体类型图标。
要求:图标文件需为 .svg 格式,并为其创建 React 组件。
第一步:创建 React 组件。在前端应用 src 下建议创建 assets/icons 目录及 CustomIcons.tsx 文件:
import { SvgIcon, SvgIconProps } from '@material-ui/core';
export const ExampleIcon = (props: SvgIconProps) => (
<SvgIcon {...props} viewBox="0 0 24 24">
<path
fill="currentColor"
width="1em"
height="1em"
display="inline-block"
d="M11.6335 10.8398C11.6335 11.6563 12.065 12.9922 13.0863 12.9922C14.1075 12.9922 14.539 11.6563 14.539 10.8398C14.539 10.0234 14.1075 8.6875 13.0863 8.6875C12.065 8.6875 11.6335 10.0234 11.6335 10.8398V10.8398ZM2.38419e-07 8.86719C2.38419e-07 10.1133 0.126667 11.4336 0.692709 12.5781C2.19292 15.5703 6.3175 15.5 9.27042 15.5C12.2708 15.5 16.6408 15.6055 18.2004 12.5781C18.7783 11.4453 19 10.1133 19 8.86719C19 7.23047 18.4498 5.68359 17.3573 4.42969C17.5631 3.8125 17.6621 3.16406 17.6621 2.52344C17.6621 1.68359 17.4681 1.26172 17.0842 0.5C15.291 0.5 14.1431 0.851562 12.7775 1.90625C11.6296 1.63672 10.45 1.51562 9.26646 1.51562C8.19771 1.51562 7.12104 1.62891 6.08396 1.875C4.73813 0.832031 3.59021 0.5 1.81687 0.5C1.42896 1.26172 1.23896 1.68359 1.23896 2.52344C1.23896 3.16406 1.34188 3.80078 1.54375 4.40625C0.455209 5.67188 2.38419e-07 7.23047 2.38419e-07 8.86719V8.86719ZM2.54521 10.8398C2.54521 9.125 3.60208 7.61328 5.45458 7.61328C6.20271 7.61328 6.91917 7.74609 7.67125 7.84766C8.26104 7.9375 8.85083 7.97266 9.45646 7.97266C10.0581 7.97266 10.6479 7.9375 11.2417 7.84766C11.9819 7.74609 12.7063 7.61328 13.4583 7.61328C15.3108 7.61328 16.3677 9.125 16.3677 10.8398C16.3677 14.2695 13.1852 14.7969 10.4144 14.7969H8.50646C5.72375 14.7969 2.54521 14.2734 2.54521 10.8398V10.8398ZM5.81479 8.6875C6.83604 8.6875 7.2675 10.0234 7.2675 10.8398C7.2675 11.6563 6.83604 12.9922 5.81479 12.9922C4.79354 12.9922 4.36208 11.6563 4.36208 10.8398C4.36208 10.0234 4.79354 8.6875 5.81479 8.6875Z"
/>
</SvgIcon>
);
第二步:在 App.tsx 中挂载自定义图标:
/* highlight-add-next-line */
import { ExampleIcon } from './assets/customIcons'
const app = createApp({
apis,
components: {
{/* ... */}
},
themes: [
{/* ... */}
],
/* highlight-add-start */
icons: {
github: ExampleIcon,
},
/* highlight-add-end */
bindRoutes({ bind }) {
{/* ... */}
}
})
新增图标(Adding Icons)
如果默认图标无法满足需求,可以注册更多图标,用于实体(Entity)Links 等场景。以 Material UI 的 AlarmIcon 为例:
- 打开
packages/app/src下的App.tsx; - 在其余 import 中追加:
import AlarmIcon from '@material-ui/icons/Alarm'; - 在
createApp中加入:
const app = createApp({
apis: ...,
plugins: ...,
/* highlight-add-start */
icons: {
alert: AlarmIcon,
},
/* highlight-add-end */
themes: ...,
components: ...,
});
- 现在可以在实体 Links 中通过
icon: alert引用它:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: artist-lookup
description: Artist Lookup
links:
- url: https://example.com/alert
title: Alerts
icon: alert
实际效果如下:
另一种使用方式是通过 AppContext 获取图标,适合需要在多处复用的场景:
import { useApp } from '@backstage/core-plugin-api';
const app = useApp();
const alertIcon = app.getSystemIcon('alert');
注意:如果请求的图标既不在默认图标中、也未注册,系统会回退到 Material UI 的
LanguageIcon。
自定义侧边栏:子菜单(Sidebar Sub-menu)
除了主题,Backstage 还提供了大量外观定制能力,侧边栏就是其中之一。下面演示如何为侧边栏添加子菜单。
- 打开
packages/app/src/components/Root下的Root.tsx(侧边栏代码所在文件); - 添加
useApp导入:
import { useApp } from '@backstage/core-plugin-api';
- 更新
@backstage/core-components导入,加入子菜单相关组件:
import {
Sidebar,
sidebarConfig,
SidebarDivider,
SidebarGroup,
SidebarItem,
SidebarPage,
SidebarScrollWrapper,
SidebarSpace,
useSidebarOpenState,
Link,
/* highlight-add-start */
GroupIcon,
SidebarSubmenu,
SidebarSubmenuItem,
/* highlight-add-end */
} from '@backstage/core-components';
- 将
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />替换为带子菜单的版本:
<SidebarItem icon={HomeIcon} to="catalog" text="Home">
<SidebarSubmenu title="Catalog">
<SidebarSubmenuItem
title="Domains"
to="catalog?filters[kind]=domain"
icon={useApp().getSystemIcon('kind:domain')}
/>
<SidebarSubmenuItem
title="Systems"
to="catalog?filters[kind]=system"
icon={useApp().getSystemIcon('kind:system')}
/>
<SidebarSubmenuItem
title="Components"
to="catalog?filters[kind]=component"
icon={useApp().getSystemIcon('kind:component')}
/>
<SidebarSubmenuItem
title="APIs"
to="catalog?filters[kind]=api"
icon={useApp().getSystemIcon('kind:api')}
/>
<SidebarDivider />
<SidebarSubmenuItem
title="Resources"
to="catalog?filters[kind]=resource"
icon={useApp().getSystemIcon('kind:resource')}
/>
<SidebarDivider />
<SidebarSubmenuItem
title="Groups"
to="catalog?filters[kind]=group"
icon={useApp().getSystemIcon('kind:group')}
/>
<SidebarSubmenuItem
title="Users"
to="catalog?filters[kind]=user"
icon={useApp().getSystemIcon('kind:user')}
/>
</SidebarSubmenu>
</SidebarItem>
启动 Backstage 后,将鼠标悬停在侧边栏的 Home 选项上,即可看到带各类目(Kinds)链接的子菜单,效果如下:
这里子菜单项直接通过 useApp().getSystemIcon('kind:domain') 等调用复用了前面“图标定制”中讲解的系统图标能力——默认图标定义即包含全部 kind:* 实体类目图标。
自定义首页(Homepage)
除了自定义主题与 Logo,你还可以定制应用首页。完整的首页定制指南请参见 首页定制指南。
迁移到 Material UI v5
Backstage 现已支持 Material UI v5。如果你需要将现有应用升级到 MUI v5,可参考 迁移指南 开始操作。而 @backstage/theme 的 Unified Theme 机制(createUnifiedTheme 同时生成 v4/v5 两套主题、UnifiedThemeProvider 同时注入两套 Provider)正是这一迁移过程中保证新旧组件样式一致性的基础设施。
小结
至此,你已经掌握了旧前端系统下 Backstage 外观定制的完整链路:
- 用
createUnifiedTheme+createBaseThemeOptions创建主题,覆盖调色板、字体、页面主题(genPageTheme+shapes); - 通过
createApp的themes配置将主题接入应用,并理解UnifiedThemeProvider同时服务 v4/v5 的机制; - 用
defaultTypography局部覆盖排版,用MuiCssBaseline的styleOverrides挂载@font-face自定义字体; - 对未引用主题值的组件样式使用
components.styleOverrides覆盖; - 替换
LogoFull/LogoIcon定制 Logo,通过icons配置覆盖或新增系统图标并在实体 Links 与useApp().getSystemIcon中使用; - 用
SidebarSubmenu/SidebarSubmenuItem扩展侧边栏子菜单。
以上所有定制均以 @backstage/theme 包(packages/theme)与各应用示例文件(packages/app)为事实依据,可直接对照仓库源码逐步验证与实践。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python230
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java291
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java200
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript190
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python300
