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

rationale.mdreferences/

Rationale

Why Emotion, why styled-components stays out, and what would change this rule.

Why Emotion is the choice

  1. Nx generators are pre-configured for Emotion. nx.json pins the style preset across every generator the workspace uses:

    "@nrwl/react":  { "application": { "style": "@emotion/styled" }, "component": { "style": "@emotion/styled" }, "library": { "style": "@emotion/styled" } },
    "@nrwl/next":   { "application": { "style": "@emotion/styled" } }

    Any nx g component, nx g lib, or nx g app will scaffold an Emotion-based file. Forcing styled-components on new code would require overriding this on every generator invocation.

  2. The full Emotion stack is already wired through Next.js.

    • @emotion/react@11.11.3 — runtime
    • @emotion/styled@11.8.1 — the styled API
    • @emotion/server@11.4.0 — SSR style extraction (essential for Next.js)
    • @emotion/babel-plugin@11.7.2 — display-name + label generation for debugging
  3. StylingProviders is the project's composed Emotion stack. Located at libs/core/src/components/providers/StylingProviders/, it composes ThemeProvider + WebFontProvider + GlobalStyles in one wrapper. All theming flows through it.

  4. Storybook is wired to Emotion through libs/core/src/config/storybook/decorators/withStyling.js, which uses StylingProviders underneath. Every story decorator goes through this.

Why styled-components stays in package.json

styled-components@5.3.5 is in dependencies for historical reasons — there is legacy code somewhere in the repo that may still depend on it. The presence of the dep is the single biggest reason an LLM will reach for it: lockfile completion is a strong signal that overpowers other context.

The rule applies to new code. Existing imports of styled-components are not in scope for this skill. A separate workflow tile (workflows-migrate-styled-components) would cover any future cleanup pass.

What would change this rule

This rule is status: stable. It would only change if:

  • The codebase committed to a full migration away from Emotion (the inverse direction). Vanishingly unlikely given the Nx generator config, SSR wiring, and Storybook decorator coupling.
  • Emotion itself was deprecated or its SSR story broke under a future React/Next.js version, forcing the team to evaluate alternatives.

Neither is on the roadmap. If either becomes true, this skill's status would move to experimental while migration patterns are authored, and would be replaced (not amended) by the new direction.

Why polished for state math

State-color manipulation (hover/active darken, focus lighten, disabled transparentize) is a small surface that benefits from a focused, tree-shakeable utility. polished is in dependencies (4.2.2), is the canonical choice in Button.component.js, and its API matches the pattern used across the codebase.

chroma-js (2.4.2) is also in dependencies but for a different purpose — palette generation, color-space conversion, contrast checking. It is not used for state math. Don't conflate them.

Why the XxxCSS suffix

A css-tagged template literal returned from @emotion/react is interpolatable into any styled.x block. Naming these with a CSS suffix (EmptyButtonCSS, withWhiteBorder-style fragments) makes the type obvious at the import site:

import { EmptyButtonCSS } from '../Button.styles';
//          └── PascalCase + CSS suffix tells you: "drop me inside a styled block"

Plain const names (emptyButton) lose this signal. The convention is the type-hint.

SKILL.md

tile.json