diff --git a/packages/core/src/layout/Header/Header.tsx b/packages/core/src/layout/Header/Header.tsx index cc2c8d1182..7aafc2730e 100644 --- a/packages/core/src/layout/Header/Header.tsx +++ b/packages/core/src/layout/Header/Header.tsx @@ -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(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( + 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; @@ -159,32 +163,28 @@ export const Header: FC = ({ 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 ( - + <> - - {theme => ( -
- -
- - - -
-
{children}
-
- )} -
-
+
+
+ + + +
+
{children}
+
+ ); }; diff --git a/packages/core/src/layout/Header/Waves.test.tsx b/packages/core/src/layout/Header/Waves.test.tsx deleted file mode 100644 index 76bf5e913e..0000000000 --- a/packages/core/src/layout/Header/Waves.test.tsx +++ /dev/null @@ -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('', () => { - it('should render svg', () => { - const rendered = render(); - rendered.getByTestId('wave-svg'); - }); -}); diff --git a/packages/core/src/layout/Header/Waves.tsx b/packages/core/src/layout/Header/Waves.tsx deleted file mode 100644 index e87fc78105..0000000000 --- a/packages/core/src/layout/Header/Waves.tsx +++ /dev/null @@ -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({ - 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 = ({ theme }) => { - const classes = useStyles(); - const [backgroundColor1, backgroundColor2] = theme.gradient.colors; - const waveColor = theme.gradient.waveColor; - const [opacityStart, opacityStop] = theme.gradient.opacity; - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/packages/core/src/layout/Page/Page.tsx b/packages/core/src/layout/Page/Page.tsx index bc4175fb6c..a5f5e4fd2b 100644 --- a/packages/core/src/layout/Page/Page.tsx +++ b/packages/core/src/layout/Page/Page.tsx @@ -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.home); +export const PageThemeContext = React.createContext(pageTheme.home); const useStyles = makeStyles(() => ({ root: { @@ -38,8 +38,8 @@ type Props = { export const Page: FC = ({ theme = pageTheme.home, children }) => { const classes = useStyles(); return ( - +
{children}
-
+ ); }; diff --git a/packages/core/src/layout/Page/PageThemeProvider.ts b/packages/core/src/layout/Page/PageThemeProvider.ts index c99509513f..57411c8786 100644 --- a/packages/core/src/layout/Page/PageThemeProvider.ts +++ b/packages/core/src/layout/Page/PageThemeProvider.ts @@ -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 = { - 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 = { + 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 = { + 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 = { - 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 }; +} diff --git a/packages/core/src/layout/Page/index.ts b/packages/core/src/layout/Page/index.ts index f8fa7dc668..000a565c83 100644 --- a/packages/core/src/layout/Page/index.ts +++ b/packages/core/src/layout/Page/index.ts @@ -15,5 +15,5 @@ */ export { Page } from './Page'; -export { gradients, pageTheme } from './PageThemeProvider'; +export { pageTheme } from './PageThemeProvider'; export type { PageTheme } from './PageThemeProvider'; diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 7d38c6f223..d98b1a11e9 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -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 });