eXXcellent solutions tech blog logoabout

Backstage part 1 - corporate design theme

Cover Image for Backstage part 1 - corporate design theme
by Oliver Pehnke

In 2023, we embarked on the journey of adopting Spotify's Backstage, aiming to enhance developers' overview and streamline development processes. Because nowadays, developers grapple with numerous platforms, tools, scattered documentation, and codebases. And thats the case for a single project. As our company expanded, the number of projects and team members increased, complicating the overview even further. But you need the overview to maintain effectiveness by collaborating with those developers that can help you.


Imagine having a single app to access all projects, technologies, and team members effortlessly. Perhaps this app even features a search function to quickly find the necessary skills. We took the initial steps towards this vision with Backstage.

Our first objective right after implementing authentication was to instill confidence in the platform's output by establishing seamless integration and aligning its appearance with our corporate style. This began with the development of a tailored theme and homepage customization.


The logo is the most easy thing to change. Just replace the SVG file in the LogoFull.tsx and LogoIcon.tsx and its done.


To customize the appearance in Backstage, we began by creating a custom theme file rather than diving into CSS. We followed the instructions in the Backstage custom theme documentation, creating the file at packages/app/src/theme/ExxTheme.tsx. This theme builds upon the Backstage light theme, which itself is based on Material UI. Therefore, our task was to add color definitions for the palette entries to align with our corporate design that differed from the backstage theme.


The next step was to incorporate our local font, which wasn't covered in the documentation. As Backstage indicated its use of the Material UI Theme API, we located the missing information here: Material UI Typography documentation and added our own font-face (see code below). Our fonts are stored in the packages/app/src/fonts folder as ttf files and referenced in the theme as MuiCssBaseline.styleOverrides. The fontFamily was changed to use DIN OT.

Customized Backstage home screen;

import { createUnifiedTheme, genPageTheme, palettes, shapes } from '@backstage/theme'; export const exxTheme = createUnifiedTheme({ palette: { ...palettes.light, // based on light theme primary: { main: '#35abe2', // light blue }, secondary: { main: '#565a6e', // Darker blue grey }, error: { main: '#8c4351', }, warning: { main: '#8f5e15', }, info: { main: '#35abe2', }, success: { main: '#35abe2', }, background: { default: '#ffffff', paper: '#f4f4f4', // Light grey }, banner: { info: '#35abe2', // light blue banner error: '#8c4351', text: '#343b58', link: '#565a6e', }, errorBackground: '#8c4351', warningBackground: '#8f5e15', infoBackground: '#343b58', navigation: { submenu: { background: '#35abe2', // light blue }, background: '#f4f4f4', // Lighter grey indicator: '#9d599f', // lilac as indicator selectedColor: '#9d599f', // same as indicator color: '#0d456b', // dark blue for unselected items navItem: { hoverBackground: '#35abe2', // light blue }, }, }, pageTheme: { home: genPageTheme({ colors: ['#0d456b', '#9d599f'], shape: shapes.wave }), documentation: genPageTheme({ colors: ['#0d456b', '#9d599f'], shape: shapes.wave, }), project: genPageTheme({ colors: ['#0d456b', '#0d456b'], shape: shapes.wave, }), tool: genPageTheme({ colors: ['#9d599f', '#9d599f'], shape: shapes.round }), library: genPageTheme({ colors: ['#9d599f', '#9d599f'], shape: shapes.round, }), technique: genPageTheme({ colors: ['#9d599f', '#9d599f'], shape: shapes.round }), other: genPageTheme({ colors: ['#0d456b', '#9d599f'], shape: shapes.wave }), apis: genPageTheme({ colors: ['#0d456b', '#9d599f'], shape: shapes.wave }), }, fontFamily: '"DIN OT", Helvetica, Arial, sans-serif', components: { BackstageInfoCard: { styleOverrides: { } }, BackstageSidebarItem: { styleOverrides: { root: { textDecorationLine: 'none' } } }, MuiCssBaseline: { styleOverrides: ` @font-face { font-family: 'DIN OT'; src: url('../fonts/DINOT-Light.ttf') format('TrueType'); font-weight: 300; font-style: normal; } @font-face { font-family: 'DIN OT'; src: url('../fonts/DINOT.ttf') format('TrueType'); font-weight: 400; font-style: normal; } @font-face { font-family: 'DIN OT'; src: url('../fonts/DINOT-Medium.ttf') format('TrueType'); font-weight: 500; font-style: normal; } @font-face { font-family: 'DIN OT'; src: url('../fonts/DINOT-Bold.ttf') format('TrueType'); font-weight: 700; font-style: normal; } `, }, MuiButton: { styleOverrides: { root: { textTransform: 'none', // Remove uppercase text }, containedPrimary: { '&:hover': { backgroundColor: '#35abe2', // light blue on hover }, color: '#FFFFFF', }, containedSecondary: { '&:hover': { backgroundColor: '#35abe2', // light blue on hover }, color: '#FFFFFF', }, }, }, } });

in the packages/app/src/App.tsx we added the theme as following:

import { exxTheme } from './theme/ExxTheme'; ... const app = createApp({ apis, components: { ... }, bindRoutes({bind}) { ... }, themes: [ { id: 'exx-theme-light', title: 'Default eXXcellent light', variant: 'light', Provider: ({ children }) => <UnifiedThemeProvider theme={exxTheme} children={children} />, } ] });

Entity page

We've organized our projects into systems and components using the entity kinds, and categorizing technical dependencies like tools, libraries, languages, and techniques using entity types. This approach allows us to create components such as "React" typed as a "library," which serves as a dependency for a component of type project like our "Maxx time tracking" example. Want to learn more? Read the the detailed story as article in the news blog(in German language only).

Project has technical dependencies in backstage;


The theme (refer to the code above) assigns distinct colors to our types. These colors are applied in the header to aid navigation. For instance, a project component or system is represented by a dark blue hue, while a tool component adopts a lilac tone.


Each entity page is equipped with specific plugins tailored to our types. We've streamlined the default plugins per type to display only relevant content to the user. For instance, there's no necessity to showcase a SonarQube tab for a technical component like "React." And the graph view can be replaced by a list to display child components more readable.

React Entity page in backstage;

Sidebar Submenu

We improved the sidebar by adding a submenu for the catalog, aiming to provide a more efficient overview of the most important types with just one click (domain, system, components, user). When users click on the components, we automatically apply a filter to assist novices, as our components are categorized by type.


<SidebarGroup label="Menu" icon={<MenuIcon />}> {/* Global nav, not org-specific */} <SidebarItem icon={HomeIcon} to="/" text="Home" /> <SidebarItem icon={CategoryIcon} to="catalog" text="Catalog"> <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&filters[type]=project" icon={useApp().getSystemIcon('kind:component')} />

Sidebar in backstage;


Backstage provides a selection of components built on Material UI, simplifying the creation of a custom home page. Our priority was to prominently feature the most commonly used tools on the page and enhance the user experience with dynamic data in the future. To achieve this, we developed custom cards, including the time tracking card.

Custom Cards


import * as React from 'react'; import CardActions from '@mui/material/CardActions'; import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, useTheme } from '@material-ui/core'; import Clocksvg from './clock-2.svg'; import { InfoCard, LinkButton } from '@backstage/core-components'; const useStyles = makeStyles<BackstageTheme, { fontColor: string; backgroundImage: string }>(theme => ({ cardContent: { color: theme.palette.background.default, backgroundColor: '#35abe2', height: '80%' }, cardActions: { height: '20%' }, cardIcon: { padding: '20px', float: 'right', height: 'auto', width: '100px', } })); export default function MaxxInfoCard() { const backstageTheme = useTheme<BackstageTheme>(); const styles = useStyles({ fontColor: backstageTheme.palette.primary.main, backgroundImage: backstageTheme.palette.infoBackground }); return ( <InfoCard noPadding variant='fullHeight'> <CardContent className={styles.cardContent}> <img className={styles.cardIcon} src={Clocksvg} alt="Clock Logo" /> <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom> Timetracking </Typography> <Typography variant="h3" component="div"> .../40h </Typography> <Typography sx={{ mb: 1.5 }} color="text.secondary"> this week </Typography> </CardContent> <CardActions className={styles.cardActions}> <LinkButton target="blank" to='https://www.exxcellent.de/<path-to-maxx>'>Open maXX</LinkButton> </CardActions> </InfoCard> ); }

Homepage custom card;

Tour and Guides

Backstage being a new tool, there's a learning curve associated with each of its features. We opted against traditional lecture-style classes and instead designed our self-service portal to be self-explanatory. However, since Backstage lacks built-in online help, we addressed this gap by incorporating a tour using @reactour/tour and providing links to the techdocs documentation with various guides to assist novices.

The tour walks users through prominent sections of the homepage, including the sidebar, providing explanations of each component's functionality.


The guides comprise a curated collection of markdown files sourced from Backstage TechDocs. Each guide outlines a specific use case and offers step-by-step instructions on utilizing a feature, such as the "how to find help based on technical components" or "How to add a project to the catalog".

Guides in backstage;


Customizing Backstage using TypeScript and React offers a seamless developer experience. The time invested in this customization is valuable as it results in a visually integrated tool that feels familiar to users, thereby easing its adoption.

Given that Backstage is another addition to the pile of tools already in use by developers, a primary objective from a user experience analysis is to ensure an enjoyable experience. This can be achieved by either minimizing the reliance on other tools (confluence, im looking at you) or by offering distinct benefits through the use of Backstage.

Such a benefit could be, that in comparison to a href to the time tracking tool, it shows a summary of working hours. Maybe in a future version we even add a "book now" that will start tracking without leaving backstage at all.

Learn more

The bigger picture how we structured the entity model to support our business model is available as article in the news blog (in German).

Image sources

The cover image used in this post was created by Kevin Ku under the following license. All other images on this page were created by eXXcellent solutions under the terms of the Creative Commons Attribution 4.0 International License