Merge pull request #563 from spotify/rugvip/theme-refactor

packages/theme: refactor to differ themes by palette only, and expose nicer types
This commit is contained in:
Patrik Oldsberg
2020-04-16 16:25:13 +02:00
committed by GitHub
21 changed files with 338 additions and 851 deletions
+7 -9
View File
@@ -20,7 +20,7 @@ import {
Theme,
ThemeProvider,
} from '@material-ui/core';
import { BackstageThemeLight, BackstageThemeDark } from '@backstage/theme';
import { lightTheme, darkTheme } from '@backstage/theme';
import { createApp } from '@backstage/core';
import React, { FC } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
@@ -53,30 +53,28 @@ app.registerApis(apis);
app.registerPlugin(...Object.values(plugins));
const AppComponent = app.build();
type T = typeof BackstageThemeLight | typeof BackstageThemeDark;
const App: FC<{}> = () => {
useStyles();
const [theme, toggleTheme] = useThemeType(
localStorage.getItem('theme') || 'auto',
);
let backstageTheme: T = BackstageThemeLight;
let backstageTheme = lightTheme;
switch (theme) {
case 'light':
backstageTheme = BackstageThemeLight;
backstageTheme = lightTheme;
break;
case 'dark':
backstageTheme = BackstageThemeDark;
backstageTheme = darkTheme;
break;
default:
if (!window.matchMedia) {
backstageTheme = BackstageThemeLight;
backstageTheme = lightTheme;
break;
}
backstageTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? BackstageThemeDark
: BackstageThemeLight;
? darkTheme
: lightTheme;
break;
}
@@ -1,6 +1,6 @@
import { CssBaseline, makeStyles, ThemeProvider } from '@material-ui/core';
import { createApp } from '@backstage/core';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
import React, { FC } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import * as plugins from './plugins';
@@ -31,7 +31,7 @@ const App: FC<{}> = () => {
useStyles();
return (
<CssBaseline>
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<Router>
<AppComponent />
</Router>
@@ -2,12 +2,12 @@ import React from 'react';
import { render } from '@testing-library/react';
import WelcomePage from './WelcomePage';
import { ThemeProvider } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
describe('WelcomePage', () => {
it('should render', () => {
const rendered = render(
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<WelcomePage />
</ThemeProvider>,
);
@@ -19,13 +19,13 @@ import { render } from '@testing-library/react';
import mockFetch from 'jest-fetch-mock';
import ExampleComponent from './ExampleComponent';
import { ThemeProvider } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
describe('ExampleComponent', () => {
it('should render', () => {
mockFetch.mockResponse(() => new Promise(() => {}));
const rendered = render(
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<ExampleComponent />
</ThemeProvider>,
);
@@ -19,7 +19,7 @@ import { BackstageTheme } from '@backstage/theme';
import { Circle } from 'rc-progress';
import React, { FC } from 'react';
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
root: {
position: 'relative',
lineHeight: 0,
@@ -19,7 +19,7 @@ import { BackstageTheme } from '@backstage/theme';
import classNames from 'classnames';
import React, { FC } from 'react';
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
status: {
width: 12,
height: 12,
@@ -27,7 +27,7 @@ const errorOutlineStyles = theme => ({
});
const ErrorOutlineStyled = withStyles(errorOutlineStyles)(ErrorOutline);
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
message: {
display: 'flex',
flexDirection: 'column',
+1 -1
View File
@@ -23,7 +23,7 @@ import { Theme } from 'layout/Page/Page';
// import { Link } from 'shared/components';
import Waves from './Waves';
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
header: {
gridArea: 'pageHeader',
padding: theme.spacing(3),
+1 -1
View File
@@ -20,7 +20,7 @@ import React, { FC, useRef, useState } from 'react';
import { sidebarConfig, SidebarContext } from './config';
import { BackstageTheme } from '@backstage/theme';
const useStyles = makeStyles<typeof BackstageTheme>(theme => ({
const useStyles = makeStyles<BackstageTheme>(theme => ({
root: {
zIndex: 1000,
position: 'relative',
+2 -2
View File
@@ -1,10 +1,10 @@
import React from 'react';
import { addDecorator } from '@storybook/react';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
import { CssBaseline, ThemeProvider } from '@material-ui/core';
addDecorator(story => (
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<CssBaseline>{story()}</CssBaseline>
</ThemeProvider>
));
@@ -18,7 +18,7 @@ import React, { ComponentType, ReactNode, FunctionComponent } from 'react';
import { ThemeProvider } from '@material-ui/core';
import { MemoryRouter } from 'react-router';
import { Route } from 'react-router-dom';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
export function wrapInTestApp(
Component: ComponentType | ReactNode,
@@ -42,12 +42,10 @@ export function wrapInThemedTestApp(
component: ReactNode,
initialRouterEntries: string[] = ['/'],
) {
const themed = (
<ThemeProvider theme={BackstageTheme}>{component}</ThemeProvider>
);
const themed = <ThemeProvider theme={lightTheme}>{component}</ThemeProvider>;
return wrapInTestApp(themed, initialRouterEntries);
}
export const wrapInTheme = (component: ReactNode, theme = BackstageTheme) => (
export const wrapInTheme = (component: ReactNode, theme = lightTheme) => (
<ThemeProvider theme={theme}>{component}</ThemeProvider>
);
-260
View File
@@ -1,260 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createMuiTheme } from '@material-ui/core';
import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
const COLORS = {
PAGE_BACKGROUND: '#F8F8F8',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#171717',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
LINK_TEXT: '#0A6EBE',
LINK_TEXT_HOVER: '#2196F3',
NAMED: {
WHITE: '#FEFEFE',
},
STATUS: {
OK: '#1db855',
WARNING: '#f49b20',
ERROR: '#CA001B',
},
};
const extendedThemeConfig: BackstageMuiThemeOptions = {
props: {
MuiGrid: {
spacing: 2,
},
MuiSwitch: {
color: 'primary',
},
},
palette: {
background: {
default: COLORS.PAGE_BACKGROUND,
// @ts-ignore
informational: '#60a3cb',
},
status: {
ok: COLORS.STATUS.OK,
warning: COLORS.STATUS.WARNING,
error: COLORS.STATUS.ERROR,
running: '#BEBEBE',
pending: '#5BC0DE',
background: COLORS.NAMED.WHITE,
},
bursts: {
fontColor: COLORS.NAMED.WHITE,
slackChannelText: '#ddd',
backgroundColor: {
default: COLORS.DEFAULT_PAGE_THEME_COLOR,
},
},
// @ts-ignore
primary: {
main: blue[500],
},
border: '#E6E6E6',
textVerySubtle: '#DDD',
textSubtle: '#6E6E6E',
highlight: '#FFFBCC',
errorBackground: COLORS.ERROR_BACKGROUND_COLOR,
warningBackground: '#F59B23',
infoBackground: '#ebf5ff',
errorText: COLORS.ERROR_TEXT_COLOR,
infoText: COLORS.INFO_TEXT_COLOR,
warningText: COLORS.NAMED.WHITE,
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
background: '#333333',
},
typography: {
fontFamily: '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif',
h5: {
fontWeight: 700,
},
h4: {
fontWeight: 700,
fontSize: 28,
marginBottom: 6,
},
h3: {
fontSize: 32,
fontWeight: 700,
marginBottom: 6,
},
h2: {
fontSize: 40,
fontWeight: 700,
marginBottom: 8,
},
h1: {
fontSize: 54,
fontWeight: 700,
marginBottom: 10,
},
},
};
const createOverrides = (
theme: BackstageMuiTheme,
): Partial<BackstageMuiTheme> => {
return {
overrides: {
MuiTableRow: {
// Alternating row backgrounds
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.default,
},
},
// Use pointer for hoverable rows
hover: {
'&:hover': {
cursor: 'pointer',
},
},
// Alternating head backgrounds
head: {
'&:nth-of-type(odd)': {
backgroundColor: COLORS.NAMED.WHITE,
},
},
},
// Tables are more dense than default mui tables
MuiTableCell: {
root: {
wordBreak: 'break-word',
overflow: 'hidden',
verticalAlign: 'middle',
lineHeight: '1',
margin: 0,
padding: '8px',
borderBottom: 0,
},
head: {
wordBreak: 'break-word',
overflow: 'hidden',
color: 'rgb(179, 179, 179)',
fontWeight: 'normal',
lineHeight: '1',
},
},
MuiTabs: {
// Tabs are smaller than default mui tab rows
root: {
minHeight: 24,
},
},
MuiTab: {
// Tabs are smaller and have a hover background
root: {
color: theme.palette.link,
minHeight: 24,
textTransform: 'initial',
'&:hover': {
color: darken(theme.palette.link, 0.3),
background: lighten(theme.palette.link, 0.95),
},
[theme.breakpoints.up('md')]: {
minWidth: 120,
fontSize: theme.typography.pxToRem(14),
fontWeight: 500,
},
},
textColorPrimary: {
color: theme.palette.link,
},
},
MuiTableSortLabel: {
// No color change on hover, just rely on the arrow showing up instead.
root: {
color: 'inherit',
'&:hover': {
color: 'inherit',
},
'&:focus': {
color: 'inherit',
},
},
// Bold font for highlighting selected column
active: {
fontWeight: 'bold',
color: 'inherit',
},
},
MuiListItemText: {
dense: {
// Default dense list items to adding ellipsis for really long str...
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
},
MuiButton: {
text: {
// Text buttons have less padding by default, but we want to keep the original padding
padding: undefined,
},
},
MuiChip: {
root: {
// By default there's no margin, but it's usually wanted, so we add some trailing margin
marginRight: theme.spacing(1),
marginBottom: theme.spacing(1),
},
},
MuiCardHeader: {
root: {
// Reduce padding between header and content
paddingBottom: 0,
},
},
MuiCardActions: {
root: {
// We default to putting the card actions at the end
justifyContent: 'flex-end',
},
},
},
};
};
const extendedTheme = createMuiTheme(extendedThemeConfig) as BackstageMuiTheme;
// V1 theming
// https://material-ui-next.com/customization/themes/
// For CSS it is advised to use JSS, see https://material-ui-next.com/customization/css-in-js/
const BackstageTheme: BackstageMuiTheme = {
...extendedTheme,
...createOverrides(extendedTheme),
};
// Temporary workaround for files incorrectly importing the theme directly
export const V1 = BackstageTheme;
export default BackstageTheme;
-273
View File
@@ -1,273 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createMuiTheme } from '@material-ui/core';
import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
const COLORS = {
PAGE_BACKGROUND: '#282828',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#424242',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
LINK_TEXT: '#0A6EBE',
LINK_TEXT_HOVER: '#2196F3',
NAMED: {
WHITE: '#FEFEFE',
},
STATUS: {
OK: '#1db855',
WARNING: '#f49b20',
ERROR: '#CA001B',
},
};
const extendedThemeConfig: BackstageMuiThemeOptions = {
props: {
MuiGrid: {
spacing: 2,
},
MuiSwitch: {
color: 'primary',
},
},
palette: {
background: {
default: COLORS.PAGE_BACKGROUND,
// @ts-ignore
informational: '#60a3cb',
},
color: {
default: '#fff',
},
type: 'dark',
status: {
ok: COLORS.STATUS.OK,
warning: COLORS.STATUS.WARNING,
error: COLORS.STATUS.ERROR,
running: '#BEBEBE',
pending: '#5BC0DE',
background: COLORS.NAMED.WHITE,
},
bursts: {
fontColor: COLORS.NAMED.WHITE,
slackChannelText: '#ddd',
backgroundColor: {
default: COLORS.DEFAULT_PAGE_THEME_COLOR,
},
},
// @ts-ignore
primary: {
main: blue[500],
},
border: '#E6E6E6',
textVerySubtle: '#DDD',
textSubtle: '#6E6E6E',
highlight: '#FFFBCC',
errorBackground: COLORS.ERROR_BACKGROUND_COLOR,
warningBackground: '#F59B23',
infoBackground: '#ebf5ff',
errorText: COLORS.ERROR_TEXT_COLOR,
infoText: COLORS.INFO_TEXT_COLOR,
warningText: COLORS.NAMED.WHITE,
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
background: '#333333',
},
typography: {
fontFamily: '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif',
h5: {
fontWeight: 700,
},
h4: {
fontWeight: 700,
fontSize: 28,
marginBottom: 6,
},
h3: {
fontSize: 32,
fontWeight: 700,
marginBottom: 6,
},
h2: {
fontSize: 40,
fontWeight: 700,
marginBottom: 8,
},
h1: {
fontSize: 54,
fontWeight: 700,
marginBottom: 10,
},
},
};
const createOverrides = (theme: BackstageMuiTheme): BackstageMuiTheme => {
return {
overrides: {
// @ts-ignore
MuiCSSBaseline: {
'@global': {
body: {
backgroundColor: theme.palette.background.default,
// @ts-ignore
color: theme.palette.color.default,
},
},
},
MuiTableRow: {
// Alternating row backgrounds
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.default,
},
},
// Use pointer for hoverable rows
hover: {
'&:hover': {
cursor: 'pointer',
},
},
// Alternating head backgrounds
head: {
'&:nth-of-type(odd)': {
backgroundColor: COLORS.NAMED.WHITE,
},
},
},
// Tables are more dense than default mui tables
MuiTableCell: {
root: {
wordBreak: 'break-word',
overflow: 'hidden',
verticalAlign: 'middle',
lineHeight: '1',
margin: 0,
padding: '8px',
borderBottom: 0,
},
head: {
wordBreak: 'break-word',
overflow: 'hidden',
color: 'rgb(179, 179, 179)',
fontWeight: 'normal',
lineHeight: '1',
},
},
MuiTabs: {
// Tabs are smaller than default mui tab rows
root: {
minHeight: 24,
},
},
MuiTab: {
// Tabs are smaller and have a hover background
root: {
color: theme.palette.link,
minHeight: 24,
textTransform: 'initial',
'&:hover': {
color: darken(theme.palette.link, 0.3),
background: lighten(theme.palette.link, 0.95),
},
[theme.breakpoints.up('md')]: {
minWidth: 120,
fontSize: theme.typography.pxToRem(14),
fontWeight: 500,
},
},
textColorPrimary: {
color: theme.palette.link,
},
},
MuiTableSortLabel: {
// No color change on hover, just rely on the arrow showing up instead.
root: {
color: 'inherit',
'&:hover': {
color: 'inherit',
},
'&:focus': {
color: 'inherit',
},
},
// Bold font for highlighting selected column
active: {
fontWeight: 'bold',
color: 'inherit',
},
},
MuiListItemText: {
dense: {
// Default dense list items to adding ellipsis for really long str...
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
},
MuiButton: {
text: {
// Text buttons have less padding by default, but we want to keep the original padding
padding: undefined,
},
},
MuiChip: {
root: {
// By default there's no margin, but it's usually wanted, so we add some trailing margin
marginRight: theme.spacing(1),
marginBottom: theme.spacing(1),
},
},
MuiCardHeader: {
root: {
// Reduce padding between header and content
paddingBottom: 0,
},
},
MuiCardActions: {
root: {
// We default to putting the card actions at the end
justifyContent: 'flex-end',
},
},
},
};
};
const extendedTheme = createMuiTheme(extendedThemeConfig) as BackstageMuiTheme;
// V1 theming
// https://material-ui-next.com/customization/themes/
// For CSS it is advised to use JSS, see https://material-ui-next.com/customization/css-in-js/
const BackstageThemeDark = {
...extendedTheme,
...createOverrides(extendedTheme),
};
// Temporary workaround for files incorrectly importing the theme directly
export const V1 = BackstageThemeDark;
export default BackstageThemeDark;
-274
View File
@@ -1,274 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createMuiTheme } from '@material-ui/core';
import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
import { blue, yellow } from '@material-ui/core/colors';
import { BackstageMuiTheme, BackstageMuiThemeOptions } from './types';
const COLORS = {
PAGE_BACKGROUND: '#F8F8F8',
DEFAULT_PAGE_THEME_COLOR: '#7C3699',
DEFAULT_PAGE_THEME_LIGHT_COLOR: '#ECDBF2',
SIDEBAR_BACKGROUND_COLOR: '#171717',
ERROR_BACKGROUND_COLOR: '#FFEBEE',
ERROR_TEXT_COLOR: '#CA001B',
INFO_TEXT_COLOR: '#004e8a',
LINK_TEXT: '#0A6EBE',
LINK_TEXT_HOVER: '#2196F3',
NAMED: {
WHITE: '#FEFEFE',
},
STATUS: {
OK: '#1db855',
WARNING: '#f49b20',
ERROR: '#CA001B',
},
};
const extendedThemeConfig: BackstageMuiThemeOptions = {
props: {
MuiGrid: {
spacing: 2,
},
MuiSwitch: {
color: 'primary',
},
},
palette: {
background: {
default: COLORS.PAGE_BACKGROUND,
// @ts-ignore
informational: '#60a3cb',
},
color: {
default: '#000',
},
status: {
ok: COLORS.STATUS.OK,
warning: COLORS.STATUS.WARNING,
error: COLORS.STATUS.ERROR,
running: '#BEBEBE',
pending: '#5BC0DE',
background: COLORS.NAMED.WHITE,
},
bursts: {
fontColor: COLORS.NAMED.WHITE,
slackChannelText: '#ddd',
backgroundColor: {
default: COLORS.DEFAULT_PAGE_THEME_COLOR,
},
},
// @ts-ignore
primary: {
main: blue[500],
},
border: '#E6E6E6',
textVerySubtle: '#DDD',
textSubtle: '#6E6E6E',
highlight: '#FFFBCC',
errorBackground: COLORS.ERROR_BACKGROUND_COLOR,
warningBackground: '#F59B23',
infoBackground: '#ebf5ff',
errorText: COLORS.ERROR_TEXT_COLOR,
infoText: COLORS.INFO_TEXT_COLOR,
warningText: COLORS.NAMED.WHITE,
linkHover: COLORS.LINK_TEXT_HOVER,
link: COLORS.LINK_TEXT,
gold: yellow.A700,
sidebar: COLORS.SIDEBAR_BACKGROUND_COLOR,
},
navigation: {
width: 220,
background: '#333333',
},
typography: {
fontFamily: '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif',
h5: {
fontWeight: 700,
},
h4: {
fontWeight: 700,
fontSize: 28,
marginBottom: 6,
},
h3: {
fontSize: 32,
fontWeight: 700,
marginBottom: 6,
},
h2: {
fontSize: 40,
fontWeight: 700,
marginBottom: 8,
},
h1: {
fontSize: 54,
fontWeight: 700,
marginBottom: 10,
},
},
};
const createOverrides = (
theme: BackstageMuiTheme,
): Partial<BackstageMuiTheme> => {
return {
overrides: {
// @ts-ignore
MuiCSSBaseline: {
'@global': {
body: {
backgroundColor: theme.palette.background.default,
// @ts-ignore
color: theme.palette.color.default,
},
},
},
MuiTableRow: {
// Alternating row backgrounds
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.default,
},
},
// Use pointer for hoverable rows
hover: {
'&:hover': {
cursor: 'pointer',
},
},
// Alternating head backgrounds
head: {
'&:nth-of-type(odd)': {
backgroundColor: COLORS.NAMED.WHITE,
},
},
},
// Tables are more dense than default mui tables
MuiTableCell: {
root: {
wordBreak: 'break-word',
overflow: 'hidden',
verticalAlign: 'middle',
lineHeight: '1',
margin: 0,
padding: '8px',
borderBottom: 0,
},
head: {
wordBreak: 'break-word',
overflow: 'hidden',
color: 'rgb(179, 179, 179)',
fontWeight: 'normal',
lineHeight: '1',
},
},
MuiTabs: {
// Tabs are smaller than default mui tab rows
root: {
minHeight: 24,
},
},
MuiTab: {
// Tabs are smaller and have a hover background
root: {
color: theme.palette.link,
minHeight: 24,
textTransform: 'initial',
'&:hover': {
color: darken(theme.palette.link, 0.3),
background: lighten(theme.palette.link, 0.95),
},
[theme.breakpoints.up('md')]: {
minWidth: 120,
fontSize: theme.typography.pxToRem(14),
fontWeight: 500,
},
},
textColorPrimary: {
color: theme.palette.link,
},
},
MuiTableSortLabel: {
// No color change on hover, just rely on the arrow showing up instead.
root: {
color: 'inherit',
'&:hover': {
color: 'inherit',
},
'&:focus': {
color: 'inherit',
},
},
// Bold font for highlighting selected column
active: {
fontWeight: 'bold',
color: 'inherit',
},
},
MuiListItemText: {
dense: {
// Default dense list items to adding ellipsis for really long str...
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
},
MuiButton: {
text: {
// Text buttons have less padding by default, but we want to keep the original padding
padding: undefined,
},
},
MuiChip: {
root: {
// By default there's no margin, but it's usually wanted, so we add some trailing margin
marginRight: theme.spacing(1),
marginBottom: theme.spacing(1),
},
},
MuiCardHeader: {
root: {
// Reduce padding between header and content
paddingBottom: 0,
},
},
MuiCardActions: {
root: {
// We default to putting the card actions at the end
justifyContent: 'flex-end',
},
},
},
};
};
const extendedTheme = createMuiTheme(extendedThemeConfig) as BackstageMuiTheme;
// V1 theming
// https://material-ui-next.com/customization/themes/
// For CSS it is advised to use JSS, see https://material-ui-next.com/customization/css-in-js/
const BackstageThemeLight = {
...extendedTheme,
...createOverrides(extendedTheme),
};
// Temporary workaround for files incorrectly importing the theme directly
export const V1 = BackstageThemeLight;
export default BackstageThemeLight;
+197
View File
@@ -0,0 +1,197 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createMuiTheme } from '@material-ui/core';
import { darken, lighten } from '@material-ui/core/styles/colorManipulator';
import { Overrides } from '@material-ui/core/styles/overrides';
import {
BackstageTheme,
BackstageThemeOptions,
BackstagePaletteOptions,
} from './types';
export function createThemeOptions(
palette: BackstagePaletteOptions,
): BackstageThemeOptions {
return {
palette,
props: {
MuiGrid: {
spacing: 2,
},
MuiSwitch: {
color: 'primary',
},
},
typography: {
fontFamily: '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif',
h5: {
fontWeight: 700,
},
h4: {
fontWeight: 700,
fontSize: 28,
marginBottom: 6,
},
h3: {
fontSize: 32,
fontWeight: 700,
marginBottom: 6,
},
h2: {
fontSize: 40,
fontWeight: 700,
marginBottom: 8,
},
h1: {
fontSize: 54,
fontWeight: 700,
marginBottom: 10,
},
},
};
}
export function createThemeOverrides(theme: BackstageTheme): Overrides {
return {
MuiTableRow: {
// Alternating row backgrounds
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.default,
},
},
// Use pointer for hoverable rows
hover: {
'&:hover': {
cursor: 'pointer',
},
},
// Alternating head backgrounds
head: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.background.paper,
},
},
},
// Tables are more dense than default mui tables
MuiTableCell: {
root: {
wordBreak: 'break-word',
overflow: 'hidden',
verticalAlign: 'middle',
lineHeight: '1',
margin: 0,
padding: '8px',
borderBottom: 0,
},
head: {
wordBreak: 'break-word',
overflow: 'hidden',
color: 'rgb(179, 179, 179)',
fontWeight: 'normal',
lineHeight: '1',
},
},
MuiTabs: {
// Tabs are smaller than default mui tab rows
root: {
minHeight: 24,
},
},
MuiTab: {
// Tabs are smaller and have a hover background
root: {
color: theme.palette.link,
minHeight: 24,
textTransform: 'initial',
'&:hover': {
color: darken(theme.palette.link, 0.3),
background: lighten(theme.palette.link, 0.95),
},
[theme.breakpoints.up('md')]: {
minWidth: 120,
fontSize: theme.typography.pxToRem(14),
fontWeight: 500,
},
},
textColorPrimary: {
color: theme.palette.link,
},
},
MuiTableSortLabel: {
// No color change on hover, just rely on the arrow showing up instead.
root: {
color: 'inherit',
'&:hover': {
color: 'inherit',
},
'&:focus': {
color: 'inherit',
},
},
// Bold font for highlighting selected column
active: {
fontWeight: 'bold',
color: 'inherit',
},
},
MuiListItemText: {
dense: {
// Default dense list items to adding ellipsis for really long str...
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
},
MuiButton: {
text: {
// Text buttons have less padding by default, but we want to keep the original padding
padding: undefined,
},
},
MuiChip: {
root: {
// By default there's no margin, but it's usually wanted, so we add some trailing margin
marginRight: theme.spacing(1),
marginBottom: theme.spacing(1),
},
},
MuiCardHeader: {
root: {
// Reduce padding between header and content
paddingBottom: 0,
},
},
MuiCardActions: {
root: {
// We default to putting the card actions at the end
justifyContent: 'flex-end',
},
},
};
}
// Creates a Backstage MUI theme using a palette.
// The theme is created with the common Backstage options and component styles.
export function createTheme(palette: BackstagePaletteOptions): BackstageTheme {
const themeOptions = createThemeOptions(palette);
const baseTheme = createMuiTheme(themeOptions) as BackstageTheme;
const overrides = createThemeOverrides(baseTheme);
const theme = { ...baseTheme, overrides };
return theme;
}
+4 -3
View File
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { default as BackstageThemeLight } from './BackstageThemeLight';
export { default as BackstageThemeDark } from './BackstageThemeDark';
export { default as BackstageTheme } from './BackstageTheme';
export * from './themes';
export * from './baseTheme';
export * from './types';
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createTheme } from 'baseTheme';
import { blue, yellow } from '@material-ui/core/colors';
export const lightTheme = createTheme({
type: 'light',
background: {
default: '#F8F8F8',
},
status: {
ok: '#1db855',
warning: '#f49b20',
error: '#CA001B',
running: '#BEBEBE',
pending: '#5BC0DE',
background: '#FEFEFE',
},
bursts: {
fontColor: '#FEFEFE',
slackChannelText: '#ddd',
backgroundColor: {
default: '#7C3699',
},
},
primary: {
main: blue[500],
},
border: '#E6E6E6',
textVerySubtle: '#DDD',
textSubtle: '#6E6E6E',
highlight: '#FFFBCC',
errorBackground: '#FFEBEE',
warningBackground: '#F59B23',
infoBackground: '#ebf5ff',
errorText: '#CA001B',
infoText: '#004e8a',
warningText: '#FEFEFE',
linkHover: '#2196F3',
link: '#0A6EBE',
gold: yellow.A700,
sidebar: '#171717',
});
export const darkTheme = createTheme({
type: 'dark',
background: {
default: '#282828',
},
status: {
ok: '#1db855',
warning: '#f49b20',
error: '#CA001B',
running: '#BEBEBE',
pending: '#5BC0DE',
background: '#FEFEFE',
},
bursts: {
fontColor: '#FEFEFE',
slackChannelText: '#ddd',
backgroundColor: {
default: '#7C3699',
},
},
primary: {
main: blue[500],
},
border: '#E6E6E6',
textVerySubtle: '#DDD',
textSubtle: '#6E6E6E',
highlight: '#FFFBCC',
errorBackground: '#FFEBEE',
warningBackground: '#F59B23',
infoBackground: '#ebf5ff',
errorText: '#CA001B',
infoText: '#004e8a',
warningText: '#FEFEFE',
linkHover: '#2196F3',
link: '#0A6EBE',
gold: yellow.A700,
sidebar: '#424242',
});
+12 -5
View File
@@ -15,8 +15,12 @@
*/
import { Theme, ThemeOptions } from '@material-ui/core';
import {
PaletteOptions,
Palette,
} from '@material-ui/core/styles/createPalette';
export type BackstageMuiPalette = Theme['palette'] & {
type PaletteAdditions = {
status: {
ok: string;
warning: string;
@@ -48,10 +52,13 @@ export type BackstageMuiPalette = Theme['palette'] & {
};
};
export interface BackstageMuiTheme extends Theme {
palette: BackstageMuiPalette;
export type BackstagePalette = Palette & PaletteAdditions;
export type BackstagePaletteOptions = PaletteOptions & PaletteAdditions;
export interface BackstageTheme extends Theme {
palette: BackstagePalette;
}
export interface BackstageMuiThemeOptions extends ThemeOptions {
palette: Partial<BackstageMuiPalette>;
export interface BackstageThemeOptions extends ThemeOptions {
palette: BackstagePaletteOptions;
}
@@ -18,12 +18,12 @@ import React from 'react';
import { render } from '@testing-library/react';
import HomePage from './HomePage';
import { ThemeProvider } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
describe('HomePage', () => {
it('should render', () => {
const rendered = render(
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<HomePage />
</ThemeProvider>,
);
@@ -18,10 +18,7 @@ import { Sparklines, SparklinesLine, SparklinesProps } from 'react-sparklines';
import { useTheme } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
function color(
data: number[],
theme: typeof BackstageTheme,
): string | undefined {
function color(data: number[], theme: BackstageTheme): string | undefined {
const lastNum = data[data.length - 1];
if (!lastNum) return undefined;
if (lastNum >= 0.9) return theme.palette.status.ok;
@@ -30,7 +27,7 @@ function color(
}
const CategoryTrendline: FC<SparklinesProps & { title?: string }> = props => {
const theme = useTheme<typeof BackstageTheme>();
const theme = useTheme<BackstageTheme>();
if (!props.data) return null;
return (
@@ -18,7 +18,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import WelcomePage from './WelcomePage';
import { ThemeProvider } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { lightTheme } from '@backstage/theme';
import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core';
describe('WelcomePage', () => {
@@ -28,7 +28,7 @@ describe('WelcomePage', () => {
<ApiProvider
apis={ApiRegistry.from([[errorApiRef, { post: jest.fn() }]])}
>
<ThemeProvider theme={BackstageTheme}>
<ThemeProvider theme={lightTheme}>
<WelcomePage />
</ThemeProvider>
</ApiProvider>,