Skip to main content
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:
next.config.ts
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:
src/pages/_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.

Build docs developers (and LLMs) love