CtrlK
BlogDocsLog inGet started
Tessl Logo

likethemammal/emotion-required

Enforce Emotion (@emotion/styled and @emotion/react) as the only styling library; forbid styled-components usage.

95

1.61x
Quality

97%

Does it follow best practices?

Impact

92%

1.61x

Average score across 4 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

bad-examples.mdreferences/

Bad examples

Common wrong shapes — anti-patterns the agent will reach for if not explicitly told otherwise. None of these are allowed in new code.

1. Importing 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.

2. Importing 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.

3. Using the 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.

4. Mixing 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.

5. Using 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.

6. Inlining theme rather than reaching for the wrapped provider

// ✗ 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>

7. Defining shared styles as plain object literals or string constants

// ✗ 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;
`;

SKILL.md

tile.json