client/components/shared/Ribbon.tsx (84 lines of code) (raw):
import type { SerializedStyles } from '@emotion/react';
import { css } from '@emotion/react';
import {
palette,
space,
textSansBold15,
textSansBold17,
} from '@guardian/source/foundations';
import type { ReactElement } from 'react';
type IconSide = 'left' | 'right';
const ribbonTailCss = css`
clip-path: polygon(100vw 0, 0 0, var(--r) 50%, 0 100%, 100vw 100%);
border-image: conic-gradient(var(--ribbonColour) 0 0) fill 0;
`;
const ribbonIconLeft = css`
padding-left: calc(var(--r) + ${space[5]}px);
`;
const ribbonShapeCss = css`
display: inline-flex;
${textSansBold17};
color: var(--copyColour);
background-color: var(--ribbonColour);
--r: 0.8em;
padding-left: calc(var(--r) + ${space[2]}px);
padding-right: ${space[5]}px;
line-height: 1.8;
width: fit-content;
`;
const smallOverrideCss = css`
${textSansBold15};
line-height: 1.8;
`;
const ribbonRoundedCornersLeftCss = css`
border-top-left-radius: ${space[1]}px;
border-bottom-left-radius: ${space[1]}px;
`;
const ribbonRoundedCornersRightCss = css`
border-top-right-radius: ${space[1]}px;
border-bottom-right-radius: ${space[1]}px;
`;
export const Ribbon = ({
copy,
ribbonColour,
copyColour,
icon,
iconSide,
withoutTail,
small,
roundedCornersLeft,
roundedCornersRight,
additionalCss,
}: {
copy: string;
ribbonColour?: string;
copyColour?: string;
icon?: ReactElement;
iconSide?: IconSide;
withoutTail?: true;
small?: true;
roundedCornersLeft?: true;
roundedCornersRight?: true;
additionalCss?: SerializedStyles;
}) => {
return (
<div
css={[
ribbonShapeCss,
roundedCornersLeft && ribbonRoundedCornersLeftCss,
roundedCornersRight && ribbonRoundedCornersRightCss,
icon && iconSide === 'left' && ribbonIconLeft,
!withoutTail && ribbonTailCss,
small && smallOverrideCss,
additionalCss,
]}
style={{
['--ribbonColour' as string]:
ribbonColour || palette.neutral[10],
['--copyColour' as string]: copyColour || palette.neutral[100],
}}
>
{iconSide !== 'right' && icon}
{copy}
{iconSide === 'right' && icon}
</div>
);
};