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
Common wrong shapes — anti-patterns the agent will reach for if not explicitly told otherwise. None of these are allowed in new code.
styled from styled-components// ✗ wrong
import styled from 'styled-components';
const Button = styled.button`
background: blue;
`;// ✓ right
import styled from '@emotion/styled';
const Button = styled.button`
background: blue;
`;The styled-components package is in package.json (5.3.5) as a legacy dep — the agent will see it in lockfile completion and assume it's the styling library. It is not.
css, keyframes, or ThemeProvider from styled-components// ✗ wrong
import { css, keyframes, ThemeProvider } from 'styled-components';// ✓ right
import { css, keyframes } from '@emotion/react';
import StylingProviders from '@codebase/core/components/providers/StylingProviders';For theming, never use a raw ThemeProvider (from either Emotion or styled-components). Always go through the project-wrapped StylingProviders, which composes the theme, web fonts, and global styles.
styled-components .attrs() API// ✗ wrong — .attrs() is styled-components-specific
const PasswordInput = styled.input.attrs({
type: 'password',
autoComplete: 'off',
})`
font-family: monospace;
`;// ✓ right — pass attrs in JSX or via a default-prop wrapper
const StyledInput = styled.input`
font-family: monospace;
`;
const PasswordInput = (props) => (
<StyledInput type="password" autoComplete="off" {...props}/>
);Emotion does not have an equivalent of .attrs(). Apply defaults in a wrapping component or in the JSX site.
styled-components and Emotion in the same file// ✗ wrong — never mix
import styled from 'styled-components';
import { css } from '@emotion/react';
const Box = styled.div`
${css`color: red;`}; // won't compose correctly across libs
`;// ✓ right — pick Emotion for everything in the file
import styled from '@emotion/styled';
import { css } from '@emotion/react';Their styled-component objects, css returns, and label generation are not interoperable.
chroma-js for hover/active state math// ✗ wrong — chroma-js is for color palettes, not state
import chroma from 'chroma-js';
const Button = styled.button`
background: ${({ bg }) => bg};
&:hover { background: ${({ bg }) => chroma(bg).darken(0.3).hex()}; }
`;// ✓ right — polished is the canonical state-math lib
import { darken } from 'polished';
const Button = styled.button`
background: ${({ bg }) => bg};
&:hover { background: ${({ bg }) => darken(0.03, bg)}; }
`;The canonical hover delta is darken(0.03, bg); active is darken(0.05, bg). See Button.component.js.
// ✗ wrong — Emotion's raw ThemeProvider bypasses the project's wrapping
import { ThemeProvider } from '@emotion/react';
<ThemeProvider theme={{ colors: { ... } }}>
<App/>
</ThemeProvider>// ✓ right — go through the wrapped provider, which also wires global styles and webfonts
import StylingProviders from '@codebase/core/components/providers/StylingProviders';
<StylingProviders theme={{ colors: { ... } }}>
<App/>
</StylingProviders>// ✗ wrong — loses interpolation, prop access, and Emotion's auto-prefixing
const sharedStyles = `
font-family: sans-serif;
font-size: 1.2rem;
`;
const Button = styled.button`${sharedStyles}`;// ✓ right — use the `css` tagged template and the XxxCSS suffix convention
import { css } from '@emotion/react';
export const EmptyButtonCSS = css`
font-family: sans-serif;
font-size: 1.2rem;
`;