Update header background shapes and colors (#2015)

* Update header background shapes and colors

* Remove an unused type frmo PageThemeProvider

* Remove Waves.test as there is no more Waves.jsx
This commit is contained in:
Niklas Ek
2020-08-19 09:34:15 +02:00
committed by GitHub
parent b569f44a38
commit 4618f0b76a
7 changed files with 122 additions and 325 deletions
+68 -68
View File
@@ -14,59 +14,63 @@
* limitations under the License.
*/
import React, { Fragment, ReactNode, CSSProperties, FC } from 'react';
import React, { ReactNode, CSSProperties, FC, useContext } from 'react';
import { Helmet } from 'react-helmet';
import { Typography, Tooltip, makeStyles } from '@material-ui/core';
import { BackstageTheme } from '@backstage/theme';
import { Theme } from '../Page/Page';
import { Waves } from './Waves';
import { PageThemeContext } from '../Page/Page';
const useStyles = makeStyles<BackstageTheme>(theme => ({
header: {
gridArea: 'pageHeader',
padding: theme.spacing(3),
minHeight: 118,
width: '100%',
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
position: 'relative',
zIndex: 100,
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'flex-end',
alignItems: 'center',
},
leftItemsBox: {
flex: '1 1 auto',
},
rightItemsBox: {
flex: '0 1 auto',
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
marginRight: theme.spacing(1),
},
title: {
color: theme.palette.bursts.fontColor,
lineHeight: '1.0em',
wordBreak: 'break-all',
fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))',
marginBottom: theme.spacing(1),
},
subtitle: {
color: 'rgba(255, 255, 255, 0.8)',
lineHeight: '1.0em',
},
type: {
textTransform: 'uppercase',
fontSize: 11,
opacity: 0.8,
marginBottom: theme.spacing(1),
color: theme.palette.bursts.fontColor,
},
}));
const useStyles = makeStyles<BackstageTheme, { backgroundImage: string }>(
theme => ({
header: {
gridArea: 'pageHeader',
padding: theme.spacing(3),
minHeight: 118,
width: '100%',
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
position: 'relative',
zIndex: 100,
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'flex-end',
alignItems: 'center',
backgroundImage: props => props.backgroundImage,
backgroundPosition: 'center',
backgroundSize: '100% 400px',
},
leftItemsBox: {
flex: '1 1 auto',
},
rightItemsBox: {
flex: '0 1 auto',
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
marginRight: theme.spacing(1),
},
title: {
color: theme.palette.bursts.fontColor,
lineHeight: '1.0em',
wordBreak: 'break-all',
fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))',
marginBottom: theme.spacing(1),
},
subtitle: {
color: 'rgba(255, 255, 255, 0.8)',
lineHeight: '1.0em',
},
type: {
textTransform: 'uppercase',
fontSize: 11,
opacity: 0.8,
marginBottom: theme.spacing(1),
color: theme.palette.bursts.fontColor,
},
}),
);
type HeaderStyles = ReturnType<typeof useStyles>;
@@ -159,32 +163,28 @@ export const Header: FC<Props> = ({
type,
typeLink,
}) => {
const classes = useStyles();
const theme = useContext(PageThemeContext);
const classes = useStyles({ backgroundImage: theme.backgroundImage });
const documentTitle = pageTitleOverride || title;
const pageTitle = title || pageTitleOverride;
const titleTemplate = `${documentTitle} | %s | Backstage`;
const defaultTitle = `${documentTitle} | Backstage`;
return (
<Fragment>
<>
<Helmet titleTemplate={titleTemplate} defaultTitle={defaultTitle} />
<Theme.Consumer>
{theme => (
<header style={style} className={classes.header}>
<Waves theme={theme} />
<div className={classes.leftItemsBox}>
<TypeFragment classes={classes} type={type} typeLink={typeLink} />
<TitleFragment
classes={classes}
pageTitle={pageTitle}
tooltip={tooltip}
/>
<SubtitleFragment classes={classes} subtitle={subtitle} />
</div>
<div className={classes.rightItemsBox}>{children}</div>
</header>
)}
</Theme.Consumer>
</Fragment>
<header style={style} className={classes.header}>
<div className={classes.leftItemsBox}>
<TypeFragment classes={classes} type={type} typeLink={typeLink} />
<TitleFragment
classes={classes}
pageTitle={pageTitle}
tooltip={tooltip}
/>
<SubtitleFragment classes={classes} subtitle={subtitle} />
</div>
<div className={classes.rightItemsBox}>{children}</div>
</header>
</>
);
};
@@ -1,27 +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 React from 'react';
import { render } from '@testing-library/react';
import { pageTheme } from '../Page/PageThemeProvider';
import { Waves } from './Waves';
describe('<Waves/>', () => {
it('should render svg', () => {
const rendered = render(<Waves theme={pageTheme.home} />);
rendered.getByTestId('wave-svg');
});
});
-148
View File
@@ -1,148 +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 React, { FC } from 'react';
import { makeStyles } from '@material-ui/core';
import { PageTheme } from '../Page';
const useStyles = makeStyles<PageTheme>({
wave: {
position: 'absolute',
height: '100%',
width: '100%',
top: 0,
bottom: 0,
left: 0,
right: 0,
zIndex: -1,
},
});
type Props = {
theme: PageTheme;
};
export const Waves: FC<Props> = ({ theme }) => {
const classes = useStyles();
const [backgroundColor1, backgroundColor2] = theme.gradient.colors;
const waveColor = theme.gradient.waveColor;
const [opacityStart, opacityStop] = theme.gradient.opacity;
return (
<svg
viewBox="0 0 1440 94"
preserveAspectRatio="xMinYMin slice"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={classes.wave}
data-testid="wave-svg"
>
<rect width="1440" height="94" fill="url(#headerGradient)" />
<g opacity="0.8">
<mask
id="mask0"
mask-type="alpha"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="1440"
height="94"
>
<rect width="1440" height="94" fill="#1F8A77" />
</mask>
<g mask="url(#mask0)">
<path
d="M710.947 14.3685C605.39 154.457 195.464 165.759 92 154.301V210.649L2330 287V222.126C1516.52 222.126 1394.17 80.1091 1161.6 -40.9544C926.122 -163.528 768.278 -61.7182 710.947 14.3685Z"
fill="url(#paint1_linear)"
/>
<path
d="M1163.25 124.34C1058.49 -16.113 651.678 -27.4441 549 -15.9565V-72.4504L2770 -149V-83.9579C1962.69 -83.9579 1841.28 58.4283 1610.47 179.806C1376.79 302.698 1220.14 200.624 1163.25 124.34Z"
fill="url(#paint2_linear)"
/>
<path
d="M-104.701 -5.00477C-224.644 62.3106 -690.435 67.7413 -808 62.2356V89.3117L1735 126V94.827C810.651 94.827 671.63 26.5849 407.363 -31.5885C139.798 -90.4876 -39.557 -41.5659 -104.701 -5.00477Z"
fill="url(#paint3_linear)"
/>
<path
d="M-247.511 -32.6315C-338.493 107.457 -691.821 118.759 -781 107.301V163.649L1148 240V175.126C446.833 175.126 341.378 33.1091 140.917 -87.9544C-62.0447 -210.528 -198.095 -108.718 -247.511 -32.6315Z"
fill="url(#paint4_linear)"
/>
</g>
</g>
<defs>
<linearGradient id="headerGradient">
<stop offset="0%" stopColor={backgroundColor1} />
<stop offset="100" stopColor={backgroundColor2} />
</linearGradient>
<linearGradient
id="paint0_linear"
x1="7.21415e-07"
y1="61"
x2="1649"
y2="59.5001"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={waveColor} stopOpacity={opacityStart} />
<stop offset="1" stopColor={waveColor} stopOpacity={opacityStop} />
</linearGradient>
<linearGradient
id="paint1_linear"
x1="1318.25"
y1="-285.884"
x2="1311.42"
y2="241.925"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={waveColor} stopOpacity={opacityStart} />
<stop offset="1" stopColor={waveColor} stopOpacity={opacityStop} />
</linearGradient>
<linearGradient
id="paint2_linear"
x1="1765.93"
y1="425.372"
x2="1759.02"
y2="-103.806"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={waveColor} stopOpacity={opacityStart} />
<stop offset="1" stopColor={waveColor} stopOpacity={opacityStop} />
</linearGradient>
<linearGradient
id="paint3_linear"
x1="585.365"
y1="-149.282"
x2="583.977"
y2="104.375"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={waveColor} stopOpacity={opacityStart} />
<stop offset="1" stopColor={waveColor} stopOpacity={opacityStop} />
</linearGradient>
<linearGradient
id="paint4_linear"
x1="275.941"
y1="-332.884"
x2="268.016"
y2="194.894"
gradientUnits="userSpaceOnUse"
>
<stop stopColor={waveColor} stopOpacity={opacityStart} />
<stop offset="1" stopColor={waveColor} stopOpacity={opacityStop} />
</linearGradient>
</defs>
</svg>
);
};
+3 -3
View File
@@ -18,7 +18,7 @@ import React, { FC } from 'react';
import { PageTheme, pageTheme } from './PageThemeProvider';
import { makeStyles } from '@material-ui/core';
export const Theme = React.createContext<PageTheme>(pageTheme.home);
export const PageThemeContext = React.createContext<PageTheme>(pageTheme.home);
const useStyles = makeStyles(() => ({
root: {
@@ -38,8 +38,8 @@ type Props = {
export const Page: FC<Props> = ({ theme = pageTheme.home, children }) => {
const classes = useStyles();
return (
<Theme.Provider value={theme}>
<PageThemeContext.Provider value={theme}>
<div className={classes.root}>{children}</div>
</Theme.Provider>
</PageThemeContext.Provider>
);
};
@@ -14,87 +14,59 @@
* limitations under the License.
*/
export type Gradient = {
colors: string[];
waveColor: string;
opacity: string[];
};
export type PageTheme = {
gradient: Gradient;
colors: string[];
shape: string;
backgroundImage: string;
};
export const gradients: Record<string, Gradient> = {
darkGrey: {
colors: ['#171717', '#383838'],
waveColor: '#757575',
opacity: ['1.0', '0.0'],
},
marineBlue: {
colors: ['#00759A', '#004EAC'],
waveColor: '#BDDBFF',
opacity: ['0.72', '0.0'],
},
veryBlue: {
colors: ['#0B2B9C', '#311288'],
waveColor: '#8960FD',
opacity: ['0.72', '0.0'],
},
rubyRed: {
colors: ['#A4284B', '#8D1134'],
waveColor: '#FFBFF5',
opacity: ['0.28', '0.10'],
},
toastyOrange: {
colors: ['#CC3707', '#9A2500'],
waveColor: '#FF784E',
opacity: ['0.72', '0.0'],
},
purpleSky: {
colors: ['#AF29F8', '#4100F4'],
waveColor: '#AF29F8',
opacity: ['0.72', '0.0'],
},
eveningSea: {
colors: ['#00FFF2', '#035355'],
waveColor: '',
opacity: ['0.72', '0.0'],
},
teal: {
colors: ['#005E4D', '#004E40'],
waveColor: '#9BF0E1',
opacity: ['0.72', '0.0'],
},
pinkSea: {
colors: ['#C8077A', '#C2297D'],
waveColor: '#ea93c3',
opacity: ['0.8', '0.0'],
},
/*
# How to add a shape
1. Get the svg shape from figma, should be ~1400 wide, ~400 high
and only the white->transparent mask, no colors.
2. Run it through https://jakearchibald.github.io/svgomg/
3. Run that through https://github.com/tigt/mini-svg-data-uri
with something like https://npm.runkit.com/mini-svg-data-uri
4. Wrap the output in `url("")`
5. Give it a name and paste it into the `shapes` object below.
*/
export const shapes: Record<string, string> = {
wave: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368' height='400' fill='none'%3e%3cmask id='a' width='1368' height='401' x='0' y='0' maskUnits='userSpaceOnUse'%3e%3cpath fill='url(%23paint0_linear)' d='M437 116C223 116 112 0 112 0h1256v400c-82 0-225-21-282-109-112-175-436-175-649-175z'/%3e%3cpath fill='url(%23paint1_linear)' d='M1368 400V282C891-29 788 40 711 161 608 324 121 372 0 361v39h1368z'/%3e%3cpath fill='url(%23paint2_linear)' d='M1368 244v156H0V94c92-24 198-46 375 0l135 41c176 51 195 109 858 109z'/%3e%3cpath fill='url(%23paint3_linear)' d='M1252 400h116c-14-7-35-14-116-16-663-14-837-128-1013-258l-85-61C98 28 46 8 0 0v400h1252z'/%3e%3c/mask%3e%3cg mask='url(%23a)'%3e%3cpath fill='white' d='M-172-98h1671v601H-172z'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear' x1='602' x2='1093.5' y1='-960.5' y2='272' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear' x1='482' x2='480' y1='1058.5' y2='70.5' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear' x1='424' x2='446.1' y1='-587.5' y2='274.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint3_linear' x1='587' x2='349' y1='-1120.5' y2='341' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e")`,
wave2: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368' height='400' fill='none'%3e%3cmask id='a' width='1764' height='479' x='-229' y='-6' maskUnits='userSpaceOnUse'%3e%3cpath fill='url(%23paint0_linear)' d='M0 400h1350C1321 336 525 33 179-2c-345-34-395 236-408 402H0z'/%3e%3cpath fill='url(%23paint1_linear)' d='M1378 177v223H0V217s219 75 327 52C436 246 717-35 965 45s254 144 413 132z'/%3e%3cpath fill='url(%23paint2_linear)' d='M26 400l-78-16c-170 205-44-6-137-30l-4-1 4 1 137 30c37-45 89-110 159-201 399-514-45 238 1176-50 275-65 354-39 91 267H26z'/%3e%3c/mask%3e%3cg mask='url(%23a)'%3e%3cpath fill='white' d='M0 0h1368v400H0z'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear' x1='431' x2='397.3' y1='-599' y2='372.8' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear' x1='236.5' x2='446.6' y1='-586' y2='381.5' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear' x1='851.8' x2='640.4' y1='-867.2' y2='363.7' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e")`,
round: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='1368' height='400' fill='none'%3e%3cmask id='a' width='2269' height='1408' x='-610' y='-509' maskUnits='userSpaceOnUse'%3e%3ccircle cx='1212.8' cy='74.8' r='317.5' fill='url(%23paint0_linear)' transform='rotate(-52 1213 75)'/%3e%3ccircle cx='737.8' cy='445.8' r='317.5' fill='url(%23paint1_linear)' transform='rotate(-116 738 446)'/%3e%3ccircle cx='601.8' cy='52.8' r='418.6' fill='url(%23paint2_linear)' transform='rotate(-117 602 53)'/%3e%3ccircle cx='999.8' cy='364' r='389.1' fill='url(%23paint3_linear)' transform='rotate(31 1000 364)'/%3e%3cellipse cx='-109.2' cy='263.5' fill='url(%23paint4_linear)' rx='429.2' ry='465.8' transform='rotate(-85 -109 264)'/%3e%3c/mask%3e%3cg mask='url(%23a)'%3e%3cpath fill='white' d='M0 0h1368v400H0z'/%3e%3c/g%3e%3cdefs%3e%3clinearGradient id='paint0_linear' x1='1301.2' x2='161.4' y1='-1879.7' y2='-969.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint1_linear' x1='826.2' x2='-313.6' y1='-1508.7' y2='-598.6' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint2_linear' x1='718.4' x2='-784.3' y1='-2524' y2='-1324.2' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint3_linear' x1='1108.2' x2='-288.6' y1='-2031.1' y2='-915.9' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3clinearGradient id='paint4_linear' x1='10.4' x2='-1626.5' y1='-2603.8' y2='-1399.5' gradientUnits='userSpaceOnUse'%3e%3cstop stop-color='white'/%3e%3cstop offset='1' stop-color='white' stop-opacity='0'/%3e%3c/linearGradient%3e%3c/defs%3e%3c/svg%3e")`,
};
export const colorVariants: Record<string, string[]> = {
darkGrey: ['#171717', '#383838'],
marineBlue: ['#006D8F', '#0049A1'],
veryBlue: ['#0027AF', '#270094'],
rubyRed: ['#98002B', '#8D1134'],
toastyOrange: ['#BE2200', '#A41D00'],
purpleSky: ['#8912CA', '#3E00EA'],
eveningSea: ['#00FFF2', '#035355'],
teal: ['#005B4B'],
pinkSea: ['#C8077A', '#C2297D'],
};
export const pageTheme: Record<string, PageTheme> = {
home: {
gradient: gradients.teal,
},
documentation: {
gradient: gradients.pinkSea,
},
tool: {
gradient: gradients.purpleSky,
},
service: {
gradient: gradients.marineBlue,
},
website: {
gradient: gradients.veryBlue,
},
library: {
gradient: gradients.rubyRed,
},
other: {
gradient: gradients.darkGrey,
},
app: {
gradient: gradients.toastyOrange,
},
home: genTheme(colorVariants.teal, shapes.wave),
documentation: genTheme(colorVariants.pinkSea, shapes.wave2),
tool: genTheme(colorVariants.purpleSky, shapes.round),
service: genTheme(colorVariants.marineBlue, shapes.wave),
website: genTheme(colorVariants.veryBlue, shapes.wave),
library: genTheme(colorVariants.rubyRed, shapes.wave),
other: genTheme(colorVariants.darkGrey, shapes.wave),
app: genTheme(colorVariants.toastyOrange, shapes.wave),
};
// As the background shapes and colors are decorative, we place them onto
// the page as a css background-image instead of an html element of its own.
// Utility to not have to write colors and shapes twice.
function genTheme(colors: string[], shape: string) {
const gradientColors = colors.length === 1 ? [colors[0], colors[0]] : colors;
const gradient = `linear-gradient(90deg, ${gradientColors.join(', ')})`;
const backgroundImage = `${shape}, ${gradient}`;
return { colors, shape, backgroundImage };
}
+1 -1
View File
@@ -15,5 +15,5 @@
*/
export { Page } from './Page';
export { gradients, pageTheme } from './PageThemeProvider';
export { pageTheme } from './PageThemeProvider';
export type { PageTheme } from './PageThemeProvider';
@@ -56,7 +56,7 @@ export const TemplateCard = ({
name,
}: TemplateCardProps) => {
const theme = pageTheme[type] ?? pageTheme.other;
const [gradientStart, gradientStop] = theme.gradient.colors;
const [gradientStart, gradientStop] = theme.colors;
const classes = useStyles({ gradientStart, gradientStop });
const href = generatePath(templateRoute.path, { templateName: name });