Skip to main content
Version: Next

Emotion Styling Guidelines and Best Practices

Emotion Styling Guidelines

DO these things:

  • DO use styled when you want to include additional (nested) class selectors in your styles
  • DO use styled components when you intend to export a styled component for re-use elsewhere
  • DO use css when you want to amend/merge sets of styles compositionally
  • DO use css when you're making a small, or single-use set of styles for a component
  • DO move your style definitions from direct usage in the css prop to an external variable when they get long
  • DO prefer tagged template literals (css={css\...`}`) over style objects wherever possible for maximum style portability/consistency
  • DO use useTheme to get theme variables

DON'T do these things:

  • DON'T use styled for small, single-use style tweaks that would be easier to read/review if they were inline
  • DON'T export incomplete Ant Design components

When to use css or styled

Use css for:

  1. Small, single-use styles
import { css } from '@emotion/react';

const MyComponent = () => (
<div
css={css`
margin: 8px;
padding: 16px;
`}
>
Content
</div>
);
  1. Composing styles
const baseStyles = css`
padding: 16px;
border-radius: 4px;
`;

const primaryStyles = css`
${baseStyles}
background-color: blue;
color: white;
`;

const secondaryStyles = css`
${baseStyles}
background-color: gray;
color: black;
`;
  1. Conditional styling
const MyComponent = ({ isActive }: { isActive: boolean }) => (
<div
css={[
baseStyles,
isActive && activeStyles,
]}
>
Content
</div>
);

Use styled for:

  1. Reusable components
import styled from '@emotion/styled';

const StyledButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
background-color: ${({ theme }) => theme.colors.primary};
color: white;

&:hover {
background-color: ${({ theme }) => theme.colors.primaryDark};
}
`;
  1. Components with complex nested selectors
const StyledCard = styled.div`
padding: 16px;
border: 1px solid ${({ theme }) => theme.colors.border};

.card-header {
font-weight: bold;
margin-bottom: 8px;
}

.card-content {
color: ${({ theme }) => theme.colors.text};

p {
margin-bottom: 12px;
}
}
`;
  1. Extending Ant Design components
import { Button } from 'antd';
import styled from '@emotion/styled';

const CustomButton = styled(Button)`
border-radius: 8px;
font-weight: 600;

&.ant-btn-primary {
background: linear-gradient(45deg, #1890ff, #722ed1);
}
`;

Using Theme Variables

Always use theme variables for consistent styling:

import { useTheme } from '@emotion/react';

const MyComponent = () => {
const theme = useTheme();

return (
<div
css={css`
background-color: ${theme.colors.grayscale.light5};
color: ${theme.colors.grayscale.dark2};
padding: ${theme.gridUnit * 4}px;
border-radius: ${theme.borderRadius}px;
`}
>
Content
</div>
);
};

Common Patterns

Responsive Design

const ResponsiveContainer = styled.div`
display: flex;
flex-direction: column;

${({ theme }) => theme.breakpoints.up('md')} {
flex-direction: row;
}
`;

Animation

const FadeInComponent = styled.div`
opacity: 0;
transition: opacity 0.3s ease-in-out;

&.visible {
opacity: 1;
}
`;

Conditional Props

interface StyledDivProps {
isHighlighted?: boolean;
size?: 'small' | 'medium' | 'large';
}

const StyledDiv = styled.div<StyledDivProps>`
padding: 16px;
background-color: ${({ isHighlighted, theme }) =>
isHighlighted ? theme.colors.primary : theme.colors.grayscale.light5};

${({ size }) => {
switch (size) {
case 'small':
return css`font-size: 12px;`;
case 'large':
return css`font-size: 18px;`;
default:
return css`font-size: 14px;`;
}
}}
`;

Best Practices

1. Use Semantic CSS Properties

// ✅ Good - semantic property names
const Header = styled.h1`
font-size: ${({ theme }) => theme.typography.sizes.xl};
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;
`;

// ❌ Avoid - magic numbers
const Header = styled.h1`
font-size: 24px;
margin-bottom: 16px;
`;
// ✅ Good - grouped styles
const Card = styled.div`
/* Layout */
display: flex;
flex-direction: column;
padding: ${({ theme }) => theme.gridUnit * 4}px;

/* Appearance */
background-color: ${({ theme }) => theme.colors.grayscale.light5};
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
border-radius: ${({ theme }) => theme.borderRadius}px;

/* Typography */
font-family: ${({ theme }) => theme.typography.families.sansSerif};
color: ${({ theme }) => theme.colors.grayscale.dark2};
`;

3. Extract Complex Styles

// ✅ Good - extract complex styles to variables
const complexGradient = css`
background: linear-gradient(
135deg,
${({ theme }) => theme.colors.primary} 0%,
${({ theme }) => theme.colors.secondary} 100%
);
`;

const GradientButton = styled.button`
${complexGradient}
padding: 12px 24px;
border: none;
color: white;
`;

4. Avoid Deep Nesting

// ✅ Good - shallow nesting
const Navigation = styled.nav`
.nav-item {
padding: 8px 16px;
}

.nav-link {
color: ${({ theme }) => theme.colors.text};
text-decoration: none;
}
`;

// ❌ Avoid - deep nesting
const Navigation = styled.nav`
ul {
li {
a {
span {
color: blue; /* Too nested */
}
}
}
}
`;

Performance Tips

1. Avoid Inline Functions in CSS

// ✅ Good - external function
const getBackgroundColor = (isActive: boolean, theme: any) =>
isActive ? theme.colors.primary : theme.colors.secondary;

const Button = styled.button<{ isActive: boolean }>`
background-color: ${({ isActive, theme }) => getBackgroundColor(isActive, theme)};
`;

// ❌ Avoid - inline function (creates new function on each render)
const Button = styled.button<{ isActive: boolean }>`
background-color: ${({ isActive, theme }) =>
isActive ? theme.colors.primary : theme.colors.secondary};
`;

2. Use CSS Objects for Dynamic Styles

// For highly dynamic styles, consider CSS objects
const dynamicStyles = (props: Props) => ({
backgroundColor: props.color,
fontSize: `${props.size}px`,
// ... other dynamic properties
});

const DynamicComponent = (props: Props) => (
<div css={dynamicStyles(props)}>
Content
</div>
);