Merge pull request #22857 from awanlin/topic/unified-theme-docs

Unified Theme Doc Updates & Exported `defaultTypography`
This commit is contained in:
Patrik Oldsberg
2024-02-27 14:02:43 +01:00
committed by GitHub
5 changed files with 234 additions and 188 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/theme': patch
---
Exported `defaultTypography` to make adjusting these values in a custom theme easier
+180 -153
View File
@@ -4,56 +4,42 @@ title: Customize the look-and-feel of your App
description: Documentation on Customizing look and feel of the App
---
Backstage ships with a default theme with a light and dark mode variant. The
themes are provided as a part of the
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package,
which also includes utilities for customizing the default theme, or creating
completely new themes.
Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes.
## Creating a Custom Theme
The easiest way to create a new theme is to use the `createTheme` function
exported by the
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You
can use it to override some basic parameters of the default theme such as the
color palette and font.
The easiest way to create a new theme is to use the `createUnifiedTheme` function exported by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You can use it to override some basic parameters of the default theme such as the color palette and font.
For example, you can create a new theme based on the default light theme like
this:
For example, you can create a new theme based on the default light theme like this:
```ts
import { createTheme, lightTheme } from '@backstage/theme';
```ts title="packages/app/src/theme/myTheme.ts"
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
const myTheme = createTheme({
palette: lightTheme.palette,
const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
}),
fontFamily: 'Comic Sans MS',
defaultPageTheme: 'home',
});
```
If you want more control over the theme, and for example customize font sizes
and margins, you can use the lower-level `createThemeOverrides` function
exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme)
in combination with
[`createTheme`](https://material-ui.com/customization/theming/#createmuitheme-options-args-theme)
from [`@material-ui/core`](https://www.npmjs.com/package/@material-ui/core). See
the "Overriding Backstage and Material UI css rules" section below.
> Note: we recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized.
You can also create a theme from scratch that matches the `BackstageTheme` type
exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
See the
[Material UI docs on theming](https://material-ui.com/customization/theming/)
for more information about how that can be done.
You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the
[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done.
## Using your Custom Theme
To add a custom theme to your Backstage app, you pass it as configuration to
`createApp`.
To add a custom theme to your Backstage app, you pass it as configuration to `createApp`.
For example, adding the theme that we created in the previous section can be
done like this:
For example, adding the theme that we created in the previous section can be done like this:
```tsx
```tsx title="packages/app/src/App.tsx"
import { createApp } from '@backstage/app-defaults';
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
@@ -68,70 +54,68 @@ const app = createApp({
variant: 'light',
icon: <LightIcon />,
Provider: ({ children }) => (
<ThemeProvider theme={myTheme}>
<CssBaseline>{children}</CssBaseline>
</ThemeProvider>
<UnifiedThemeProvider theme={myTheme} children={children} />
),
}]
})
```
Note that your list of custom themes overrides the default themes. If you still
want to use the default themes, they are exported as `lightTheme` and
`darkTheme` from
[`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.light` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
## Example of a custom theme
```ts
```ts title="packages/app/src/theme/myTheme.ts"
import {
createTheme,
createBaseThemeOptions,
createUnifiedTheme,
genPageTheme,
lightTheme,
palettes,
shapes,
} from '@backstage/theme';
const myTheme = createTheme({
palette: {
...lightTheme.palette,
primary: {
main: '#343b58',
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',
},
},
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 */
@@ -161,16 +145,92 @@ const myTheme = createTheme({
});
```
For a more complete example of a custom theme including Backstage and
Material UI component overrides, see the [Aperture
theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts)
from the [Backstage demo site](https://demo.backstage.io).
For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io).
## Custom Typography
When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme:
```ts title="packages/app/src/theme/myTheme.ts"
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
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',
}),
});
```
If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this:
```ts title="packages/app/src/theme/myTheme.ts"
import {
createBaseThemeOptions,
createUnifiedTheme,
defaultTypography,
palettes,
} from '@backstage/theme';
const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
typography: {
...defaultTypography,
htmlFontSize: 16,
fontFamily: 'Roboto, sans-serif',
h1: {
fontSize: 72,
fontWeight: 700,
marginBottom: 10,
},
},
defaultPageTheme: 'home',
}),
});
```
## Overriding Backstage and Material UI components styles
When creating a custom theme you would be applying different values to
component's css rules that use the theme object. For example, a Backstage
component's styles might look like this:
When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this:
```tsx
const useStyles = makeStyles<BackstageTheme>(
@@ -185,83 +245,50 @@ const useStyles = makeStyles<BackstageTheme>(
);
```
Notice how the `padding` is getting its value from `theme.spacing`, that means
that setting a value for spacing in your custom theme would affect this
component padding property and the same goes for `backgroundImage` which uses
`theme.page.backgroundImage`. However, the `boxShadow` property doesn't
reference any value from the theme, that means that creating a custom theme
wouldn't be enough to alter the `box-shadow` property or to add css rules that
aren't already defined like a margin. For these cases you should also create an
override.
Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override.
```tsx
import { createApp } from '@backstage/core-app-api';
import { BackstageTheme, lightTheme } from '@backstage/theme';
/**
* The `@backstage/core-components` package exposes this type that
* contains all Backstage and `material-ui` components that can be
* overridden along with the classes key those components use.
*/
import { BackstageOverrides } from '@backstage/core-components';
Here's how you would do that:
export const createCustomThemeOverrides = (
theme: BackstageTheme,
): BackstageOverrides => {
return {
```ts title="packages/app/src/theme/myTheme.ts"
import {
createBaseThemeOptions,
createUnifiedTheme,
palettes,
} from '@backstage/theme';
const myTheme = createUnifiedTheme({
...createBaseThemeOptions({
palette: palettes.light,
}),
fontFamily: 'Comic Sans MS',
defaultPageTheme: 'home',
components: {
BackstageHeader: {
header: {
width: 'auto',
margin: '20px',
boxShadow: 'none',
borderBottom: `4px solid ${theme.palette.primary.main}`,
styleOverrides: {
header: ({ theme }) => ({
width: 'auto',
margin: '20px',
boxShadow: 'none',
borderBottom: `4px solid ${theme.palette.primary.main}`,
}),
},
},
};
};
const customTheme: BackstageTheme = {
...lightTheme,
overrides: {
// These are the overrides that Backstage applies to `material-ui` components
...lightTheme.overrides,
// These are your custom overrides, either to `material-ui` or Backstage components.
...createCustomThemeOverrides(lightTheme),
},
};
const app = createApp({
apis: ...,
plugins: ...,
themes: [{
id: 'my-theme',
title: 'My Custom Theme',
variant: 'light',
Provider: ({ children }) => (
<ThemeProvider theme={customTheme}>
<CssBaseline>{children}</CssBaseline>
</ThemeProvider>
),
}]
});
```
## Custom Logo
In addition to a custom theme, you can also customize the logo displayed at the
far top left of the site.
In addition to a custom theme, you can also customize the logo displayed at the far top left of the site.
In your frontend app, locate `src/components/Root/` folder. You'll find two
components:
In your frontend app, locate `src/components/Root/` folder. You'll find two components:
- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened.
- `LogoIcon.tsx` - A smaller logo used when the sidebar navigation is closed.
- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed.
To replace the images, you can simply replace the relevant code in those
components with raw SVG definitions.
To replace the images, you can simply replace the relevant code in those components with raw SVG definitions.
You can also use another web image format such as PNG by importing it. To do
this, place your new image into a new subdirectory such as
`src/components/Root/logo/my-company-logo.png`, and then add this code:
You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code:
```tsx
import MyCustomLogoFull from './logo/my-company-logo.png';
@@ -408,7 +435,7 @@ For this example we'll show you how you can expand the sidebar with a sub-menu:
3. Then update the `@backstage/core-components` import like this:
```tsx
```tsx title="packages/app/src/components/Root/Root.tsx"
import {
Sidebar,
sidebarConfig,
@@ -430,7 +457,7 @@ For this example we'll show you how you can expand the sidebar with a sub-menu:
4. Finally replace `<SidebarItem icon={HomeIcon} to="catalog" text="Home" />` with this:
```tsx
```tsx title="packages/app/src/components/Root/Root.tsx"
<SidebarItem icon={HomeIcon} to="catalog" text="Home">
<SidebarSubmenu title="Catalog">
<SidebarSubmenuItem
+3
View File
@@ -205,6 +205,9 @@ export const darkTheme: Theme_3;
// @public
export const defaultComponentThemes: ThemeOptions['components'];
// @public
export const defaultTypography: BackstageTypography;
// @public
export function genPageTheme(props: {
colors: string[];
@@ -22,6 +22,46 @@ const DEFAULT_FONT_FAMILY =
'"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif';
const DEFAULT_PAGE_THEME = 'home';
/**
* Default Typography settings.
*
* @public
*/
export const defaultTypography: BackstageTypography = {
htmlFontSize: DEFAULT_HTML_FONT_SIZE,
fontFamily: DEFAULT_FONT_FAMILY,
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,
},
};
/**
* Options for {@link createBaseThemeOptions}.
*
@@ -57,40 +97,8 @@ export function createBaseThemeOptions<PaletteOptions>(
throw new Error(`${defaultPageTheme} is not defined in pageTheme.`);
}
const defaultTypography: BackstageTypography = {
htmlFontSize,
fontFamily,
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,
},
};
defaultTypography.htmlFontSize = htmlFontSize;
defaultTypography.fontFamily = fontFamily;
return {
palette,
+4 -1
View File
@@ -14,7 +14,10 @@
* limitations under the License.
*/
export { createBaseThemeOptions } from './createBaseThemeOptions';
export {
createBaseThemeOptions,
defaultTypography,
} from './createBaseThemeOptions';
export type { BaseThemeOptionsInput } from './createBaseThemeOptions';
export { colorVariants, genPageTheme, pageTheme, shapes } from './pageTheme';
export { palettes } from './palettes';