Enforce Emotion (@emotion/styled and @emotion/react) as the only styling library; forbid styled-components usage.
95
97%
Does it follow best practices?
Impact
92%
1.61xAverage score across 4 eval scenarios
Passed
No known issues
Canonical Emotion patterns from the codebase.
styled + css + polished in a component filelibs/core/src/components/general/Button/Button.component.js:
import styled from '@emotion/styled';
import { css } from '@emotion/react';
import { darken } from 'polished';
const getButtonBackground = (background) => {
const hover = darken(0.03, background);
const active = darken(0.05, background);
return css`
background: ${background};
&:hover { background: ${hover}; }
&:active { background: ${active}; }
`;
};
export const BlankButton = styled.button`
background: none;
appearance: none;
color: inherit;
border: none;
padding: 0;
font: inherit;
outline: inherit;
&:hover { cursor: pointer; }
&:disabled {
opacity: 0.4;
&:hover { cursor: default; }
}
`;
export const SimpleButton = styled(BlankButton)`
border-radius: 4px;
padding: 0.7rem 1.6rem 0.6rem;
font-size: 1.5rem;
text-transform: uppercase;
font-weight: bold;
font-family: sans-serif;
letter-spacing: 0.02rem;
color: hsl(0, 0%, 20%);
${() => getButtonBackground('#1ba2f6')};
`;Key shapes:
@emotion/styled and @emotion/react are imported in the same filecss template literal returns a fragment that's interpolated into a styled.x via ${() => getButtonBackground(...)}darken from polished does state-color math; results are interpolated into the same css blockBlankButton) is extended via styled(BlankButton) for the skinned variant (SimpleButton) — matches the Blank/Simple primitive-prefix conventionXxxCSS suffixlibs/apps/site/src/components/Button.styles.js:
import { css } from '@emotion/react';
export const EmptyButtonCSS = css`
font-family: sans-serif;
font-size: 1.2rem;
line-height: 1.2rem;
outline: 0;
background: transparent;
color: white;
white-space: nowrap;
border: 0;
&:hover, &:active, &:focus {
text-decoration: none;
}
`;Consumed by another styles file:
libs/apps/site/src/components/Sections/Sections.styles.js:
import styled from '@emotion/styled';
import { EmptyButtonCSS } from '../Button.styles';
export const ShowMore = styled.button`
${EmptyButtonCSS};
cursor: pointer;
font-size: 1.4rem;
line-height: 1.4rem;
color: inherit;
&:hover, &:active, &:focus {
text-decoration: underline;
}
`;Key shapes:
export const named with the CSS suffix${EmptyButtonCSS}; (trailing semicolon convention) inside another styled block@emotion/react; the consuming file uses both @emotion/styled and the imported chunkstyled(Base)libs/core/src/components/themes/base/Page.styles.js:
import styled from '@emotion/styled';
import { css } from '@emotion/react';
export const Inner = styled.div`
position: relative;
padding: 0 3rem;
margin: 0 auto;
max-width: 107.5rem;
@media (max-width: 515px) {
padding: 0 2.0rem;
}
`;
export const WideInner = styled(Inner)`
max-width: 150rem;
padding: 0 4rem;
`;
export const BannerInner = styled(Inner)`
padding: 0 3.8rem;
`;Key shapes:
styled.x for the base, styled(Base) for variants<Modifier><Base> convention (WideInner extends Inner, BannerInner extends Inner).styles.js file is normal — don't pre-splitlibs/apps/site/src/components/CardGrid/CardGrid.styles.js:
import styled from '@emotion/styled';
export const LoadMoreContainer = styled.div`
justify-content: center;
padding: 0 0 2rem 0;
display: ${({ shouldLoadMore }) => shouldLoadMore ? `none` : 'flex'};
`;Key shapes:
getColor pattern in Chrome.js)libs/core/src/config/storybook/decorators/withStyling.js:
import React from 'react';
import StylingProviders from '../../../components/providers/StylingProviders';
export default ({ theme } = {}) => (Story) => {
return <StylingProviders theme={theme}>
<Story/>
</StylingProviders>;
};Key shape: stories receive Emotion theming via the wrapped StylingProviders, not via Emotion's ThemeProvider directly. This is the canonical way to expose theme to a story.