AnimeThemes Web uses styled-components for component styling, providing CSS-in-JS with full TypeScript support and theming capabilities.
Configuration
The Next.js compiler is configured to enable styled-components with SSR support:
const nextConfig: NextConfig = {
compiler: {
styledComponents: true,
},
// ... other config
};
This enables:
- Server-side rendering of styles
- Automatic critical CSS extraction
- Development-friendly class names
- Production-optimized minification
Basic Usage
Creating Styled Components
Styled components are created using tagged template literals:
import styled from "styled-components";
const StyledButton = styled.button`
padding: 8px 16px;
border-radius: 4px;
background-color: ${(props) => props.theme.colors.primary};
color: white;
border: none;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;
Transient Props
Use transient props (prefixed with $) to pass props that shouldn’t be forwarded to the DOM:
src/components/button/Button.tsx
const StyledButton = styled.button<{ $variant?: string }>`
background-color: ${
(props) => props.$variant === "primary"
? props.theme.colors["solid-primary"]
: props.theme.colors.solid
};
`;
// Usage
<StyledButton $variant="primary">Click Me</StyledButton>
Transient props (with $ prefix) are not passed to the underlying DOM element, preventing React warnings about unknown props.
Theme Integration
Accessing Theme Values
All styled components have access to the theme object via props:
const Card = styled.div`
background-color: ${(props) => props.theme.colors.solid};
box-shadow: ${(props) => props.theme.shadows.low};
border-radius: ${(props) => props.theme.scalars.borderRadiusCard};
@media (max-width: ${(props) => props.theme.breakpoints.mobileMax}) {
padding: 12px;
}
`;
Available Theme Properties
The theme provides access to:
- Colors - CSS custom properties for colors (e.g.,
var(--primary-500))
- Shadows - Pre-defined shadow styles
- Breakpoints - Responsive design breakpoints
- Z-indices - Consistent layering values
- Scalars - Common values like border radius
See Theming for complete theme documentation.
Common Patterns
Component Variants
type Variant = "primary" | "secondary" | "warning";
const Button = styled.button<{ $variant?: Variant }>`
padding: 8px 16px;
border-radius: 4px;
${(props) => {
switch (props.$variant) {
case "primary":
return `
background: ${props.theme.colors["solid-primary"]};
color: ${props.theme.colors["text-on-primary"]};
`;
case "warning":
return `
background: ${props.theme.colors["solid-warning"]};
color: ${props.theme.colors["text-on-warning"]};
`;
default:
return `
background: ${props.theme.colors.solid};
color: ${props.theme.colors.text};
`;
}
}}
`;
Extending Styled Components
const BaseButton = styled.button`
padding: 8px 16px;
border-radius: 4px;
`;
const PrimaryButton = styled(BaseButton)`
background-color: ${(props) => props.theme.colors["solid-primary"]};
color: ${(props) => props.theme.colors["text-on-primary"]};
`;
Using CSS Variables
Components can define and use CSS custom properties:
src/components/card/Card.ts
const Card = styled.article`
display: flex;
flex-direction: column;
gap: var(--gap, 16px);
padding: var(--padding, 24px);
background-color: ${(props) => props.theme.colors.solid};
border-radius: ${(props) => props.theme.scalars.borderRadiusCard};
`;
// Usage with custom gap
<Card style={{ "--gap": "8px" }}>
{children}
</Card>
Conditional Styling
const Container = styled.div<{ $isLoading?: boolean }>`
opacity: ${(props) => props.$isLoading ? 0.5 : 1};
pointer-events: ${(props) => props.$isLoading ? "none" : "auto"};
transition: opacity 0.2s;
`;
Server-Side Rendering
Styled-components are configured for SSR in _document.tsx:
import Document, { DocumentContext } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
This ensures all styles are rendered on the server and included in the initial HTML.
Best Practices
Use transient props
Prefix props with $ to avoid passing them to DOM elements
Leverage the theme
Always use theme values instead of hardcoding colors and spacing
Keep styles colocated
Place styled components near the components that use them
Use CSS variables
Define custom properties for dynamic values
For responsive styling, use the theme breakpoints rather than hardcoding media query values. This ensures consistency across the application.