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:
- Small, single-use styles
import { css } from '@emotion/react';
const MyComponent = () => (
<div
css={css`
margin: 8px;
padding: 16px;
`}
>
Content
</div>
);
- 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;
`;
- Conditional styling
const MyComponent = ({ isActive }: { isActive: boolean }) => (
<div
css={[
baseStyles,
isActive && activeStyles,
]}
>
Content
</div>
);
Use styled
for:
- 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};
}
`;
- 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;
}
}
`;
- 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``;
case 'large':
return css``;
default:
return css``;
}
}}
`;
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;
`;
2. Group Related Styles
// ✅ 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>
);