Skip to content

Latest commit

 

History

History
838 lines (653 loc) · 26.6 KB

File metadata and controls

838 lines (653 loc) · 26.6 KB

React.JS | styled-components

Objetivo

Estilizar aplicações React de maneira moderna, eficiente e modular, utilizando a biblioteca Styled-Components. Essa abordagem é baseada em CSS-in-JS, permitindo que o CSS seja escrito diretamente dentro dos componentes JavaScript.

Pré-requisitos

  • Framework x Biblioteca
  • HTML e CSS
  • Introdução ao React
  • Componentes, propriedades e estados
  • Introdução aos hooks
  • Hook useState
  • Hook useEffect
  • React Router*

Executando o projeto

Instale as dependências do projeto

yarn

Execute o projeto em um servidor de desenvolvimento

yarn dev

Introdução ao styled-components

O styled-components é uma biblioteca para React e React Native que permite estilizar componentes usando uma abordagem baseada em JavaScript e CSS. Ele utiliza tagged template literals para escrever estilos diretamente em arquivos JavaScript, encapsulando o CSS em componentes.

import styled from 'styled-components';

const Botao = styled.button`
  background-color: ${(props) => props.primary ? 'blue' : 'gray'};
  color: white;
  padding: 10px;
`;

Isso facilita a criação de estilos utilizando valores dinâmicos, reutilizáveis e elimina problemas como conflitos de nomes de seletores no CSS tradicional. Além disso, ele facilita a criação de temas na aplicação (exemplo, dark e light).


Uma das motivações do styled-components foi aprimorar a experiência para nós desenvolvedores no momento de estilizarmos nossos componentes React. Além disso, a biblioteca oferece:

  • CSS crítico automático: o styled-components rastreia quais componentes são renderizados em uma página e injeta apenas seus estilos, sem nada além disso, de forma totalmente automática, fazendo com que os usuários carreguem a menor quantidade de código necessária.
  • Sem bugs de nomes de classes: o styled-components gera nomes de classes únicos para seus estilos. Você nunca precisa se preocupar com duplicações, sobreposições ou erros de digitação.
  • Exclusão facilitada de CSS: pode ser difícil saber se um nome de classe está sendo usado em algum lugar da base de código. O styled-components torna isso evidente, já que cada estilo está vinculado a um componente específico. Se o componente não estiver mais em uso (o que ferramentas podem detectar) e for excluído, todos os seus estilos também serão removidos.
  • Estilização dinâmica simplificada: adaptar os estilos de um componente com base em suas props ou em um tema global é simples e intuitivo, sem a necessidade de gerenciar manualmente dezenas de classes.
  • Manutenção sem complicações: você nunca precisará procurar em arquivos diferentes para encontrar o estilo que está afetando seu componente, tornando a manutenção muito mais fácil, independentemente do tamanho da base de código.
  • Prefixos automáticos para navegadores: escreva seu CSS de acordo com o padrão atual, e o styled-components cuidará do restante.

Instalação

# com npm
npm install styled-components

# com yarn
yarn add styled-components

Se você estiver usando o Yarn, ele tem um recurso chamado resolutions, que permite forçar o uso de uma versão específica de uma biblioteca em todo o projeto. Isso é útil porque:

  • Em projetos grandes, pode acontecer de dependências diferentes tentarem usar versões diferentes do styled-components.
  • Essa configuração garante que todo o projeto utilize a mesma versão principal (no exemplo, a versão 5).

Exemplo de configuração no arquivo package.json:

{
  "resolutions": {
    "styled-components": "^5"
  }
}

Exemplo de utilização

O styled-components utiliza o que chamamos de tagged template literals para estilizar seus componentes.

ℹ️

Tagged Template Literals são uma funcionalidade do JavaScript que permite combinar strings escritas com crases (```` que chamamos de template literals**)** com uma função especial. Essa função consegue manipular o conteúdo da string antes de ela ser usada, incluindo os textos fixos e os valores que você insere dentro da string.

O styled-components elimina a necessidade de criar uma separação entre o CSS e os componentes no React. Em vez de escrever estilos em um arquivo CSS separado e aplicar classes, você cria diretamente um componente estilizado que já tem os estilos incorporados.

// Cria um componente Title que renderizará uma tag <h1> com alguns estilos
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: #BF4F74;
`;

// Cria um componente Wrapper que renderizará uma tag <section> com alguns estilos
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use Title e Wrapper como qualquer outro componente React – só que estilizados!
render(
  <Wrapper>
    <Title>
      Hello World!
    </Title>
  </Wrapper>
);

Adaptando estilos com base nas propriedades

Quando você cria um componente estilizado (usando, por exemplo, a biblioteca Styled Components no React), você pode personalizar o estilo desse componente com base nas propriedades que você passa para o componente.

const Button = styled.button<{ $primary?: boolean; }>`
  /* Adapt the colors based on primary prop */
  background: ${props => props.$primary ? "#BF4F74" : "white"};
  color: ${props => props.$primary ? "white" : "#BF4F74"};

  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;
`;

render(
  <div>
    <Button>Normal</Button>
    <Button $primary>Primary</Button>
  </div>
);

Estendendo estilos

Às vezes, você pode querer usar um componente, mas fazer algumas mudanças nele para um caso específico. Em vez de passar muitas funções para alterar o estilo, você pode criar um novo componente facilmente.

Para isso, basta usar o styled() e "herdar" o estilo de um componente existente.

// O botão da seção anterior sem as interpolations
const Button = styled.button`
  color: #BF4F74;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;
`;

// Um novo componente baseado no Button, mas com alguns estilos sobrescritos
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

render(
  <div>
    <Button>Botão Normal</Button>
    <TomatoButton>Botão Tomato</TomatoButton>
  </div>
);

Quando você cria um componente estilizado, ele normalmente renderiza uma tag HTML específica, como <button>, <a>, <div>, etc. Mas, em alguns casos, você pode querer que o componente renderize uma tag diferente, sem mudar o estilo dele. Por exemplo, em uma barra de navegação, você pode ter tanto links (<a>) quanto botões (<button>), mas quer que eles pareçam e se comportem de forma igual, com o mesmo estilo.

A solução para isso é usar a propriedade as. Ela permite que você escolha qual tag ou componente o styled-components vai renderizar, sem precisar alterar o estilo.

const Button = styled.button`
  display: inline-block;
  color: #BF4F74;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;
  display: block;
`;

const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

render(
  <div>
    <Button>Normal Button</Button>
    <Button as="a" href="#">Link with Button styles</Button>
    <TomatoButton as="a" href="#">Link with Tomato Button styles</TomatoButton>
  </div>
);

Esse recurso funciona perfeitamente com componentes customizados também!

const Button = styled.button`
  display: inline-block;
  color: #BF4F74;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid #BF4F74;
  border-radius: 3px;
  display: block;
`;

const ReversedButton = props => <Button {...props} children={props.children.split('').reverse()} />

render(
  <div>
    <Button>Normal Button</Button>
    <Button as={ReversedButton}>Custom Button with Normal Button styles</Button>
  </div>
);

Estilizando qualquer componente

O método styled funciona perfeitamente em qualquer componente, seja seu ou de terceiros, desde que o componente passe a prop className para um elemento DOM.

// Isso poderia ser o Link do react-router, por exemplo
const Link = ({ className, children }) => (
  <a className={className}>
    {children}
  </a>
);

const StyledLink = styled(Link)`
  color: #BF4F74;
  font-weight: bold;
`;

render(
	<div>
    <Link>Unstyled, boring Link</Link>
    <br />
    <StyledLink>Styled, exciting Link</StyledLink>
  </div>
);
💡

Você também pode passar nomes de tags na chamada ao styled(), assim: styled("div"). Na verdade, a sintaxe styled.tagname são apenas formas de escrever que fazem a mesma coisa.

Passando propriedades

Se o que for passado para o styled for um elemento simples (por exemplo, styled.div), o styled-components passa qualquer atributo HTML conhecido para o DOM. Se for um componente React personalizado (por exemplo, styled(MyComponent)), o styled-components passa todas as props do seu componene.

// Cria um componente Input que renderiza uma tag <input> com alguns estilos
const Input = styled.input<{ $inputColor?: string; }>`
  padding: 0.5em;
  margin: 0.5em;
  color: ${props => props.$inputColor || "#BF4F74"};
  background: papayawhip;
  border: none;
  border-radius: 3px;
`;

// Renderiza um campo de texto estilizado com a cor de entrada padrão, 
// e outro com uma cor personalizada
render(
  <div>
    <Input defaultValue="@probablyup" type="text" />
    <Input defaultValue="@geelen" type="text" $inputColor="rebeccapurple" />
  </div>
);

Este exemplo mostra como todas as props do componente Input são passadas para o nó do DOM que é montado, assim como nos elementos React.

Pseudo-elementos, pseudo-seletores e aninhamento

No Styled Components, o uso do & e && tem funções relacionadas ao CSS dentro do JavaScript, mas com diferenças sutis na maneira como eles são aplicados.

O & é um seletor especial que faz referência ao próprio componente ou à classe que está sendo estilizada. Ele é usado para adicionar mais especificidade ou para referenciar o estado do componente, como quando você deseja aplicar estilos para um estado de hover, foco, ou outros pseudo-seletores.

const Button = styled.button`
  background-color: blue;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;

  /* Pseudo-seletor: estado de hover */
  &:hover {
    background-color: green; /* Aplica quando o botão for hover */
    cursor: pointer; /* Muda o cursor ao passar o mouse */
  }

  /* Pseudo-seletor: quando o botão está ativo (pressionado) */
  &:active {
    background-color: darkgreen; /* Aplica quando o botão for pressionado */
  }

  /* Pseudo-classe: foco no botão */
  &:focus {
    outline: 2px solid orange; /* Aplica uma borda laranja ao focar no botão */
  }

  /* Usando a classe ".active" */
  &.active {
    background-color: red; /* Aplica quando o botão tiver a classe "active" */
  }

  /* Pseudo-elemento: antes do conteúdo do botão */
  &::before {
    content: '👉'; /* Adiciona um emoji antes do texto */
    margin-right: 8px; /* Espaçamento entre o ícone e o texto */
  }

  /* Pseudo-elemento: depois do conteúdo do botão */
  &::after {
    content: '❗'; /* Adiciona um ícone depois do texto */
    margin-left: 8px; /* Espaçamento entre o texto e o ícone */
  }

  /* Aninhamento de um elemento interno dentro do botão */
  span {
    font-weight: bold;
    font-size: 1.1rem;
  }

  /* Estilo quando o botão está desabilitado */
  &:disabled {
    background-color: lightgray;
    cursor: not-allowed; /* Indica que o botão não pode ser clicado */
  }
`;
  • O &:hover se refere ao próprio componente (Button) quando está em estado de hover.
  • O &.active se refere ao componente Button com a classe .active.

O && é uma maneira de aumentar a especificidade dos seletores, ou seja, tornar os estilos aplicados mais "fortes" e garantir que sejam aplicados com uma especificidade mais alta do que as regras que podem vir antes ou em outros lugares. Ele é comumente usado para evitar que estilos sejam sobrescritos por outras regras CSS mais genéricas ou com menor especificidade.

const Button = styled.button`
  background-color: blue;

  && {
    background-color: red; /* Aplica a regra com mais especificidade */
  }
`;

O && aqui cria um seletor de maior especificidade, o que torna difícil para outras regras CSS sobrescreverem essa.

Adicionando propriedades extras

Permite que você passe props para um componente estilizado de maneira dinâmica, com base no que é necessário em um momento específico.

Vamos criar um botão (Button) que aceita uma prop dinâmica para ajustar a largura do botão.

import styled from 'styled-components';

const Button = styled.button.attrs<{ $width?: string }> (props => ({
  // Definindo uma prop estática
  type: "button",
  
  // Definindo uma prop dinâmica
  $width: props.$width || "150px", // Se não passar, o valor padrão será "150px"
}))`
  background-color: #4CAF50;
  color: white;
  font-size: 16px;
  border: none;
  border-radius: 5px;
  cursor: pointer;

  /* Usando a prop dinâmica para ajustar a largura do botão */
  width: ${props => props.$width};
  padding: 10px;

  /* Estilos para o hover */
  &:hover {
    background-color: #45a049;
  }
  
  /* Estilos para o foco */
  &:focus {
    outline: none;
  }
`;

const App = () => {
  return (
    <div>
      <Button onClick={() => alert('Button clicked!')}>
        Default Button
      </Button>
      <br />
      <Button $width="300px" onClick={() => alert('Custom width button clicked!')}>
        Custom Width Button
      </Button>
    </div>
  );
};

export default App;

attrs: A função attrs é usada para definir props estáticas e dinâmicas. No caso, usamos:

  • A prop estática type: "button".
  • A prop dinâmica $width, que recebe um valor passado para o componente ou um valor padrão de "150px" caso não seja fornecido.

Por que definimos uma propriedade estática nesse caso?

Isso é importante para evitar comportamentos indesejados, como o envio acidental de um formulário quando você só quer que o botão execute uma função específica com um manipulador de eventos (como onClick).

Quando você está criando um botão estilizado com o styled-components, pode ser uma boa prática especificar explicitamente o tipo de botão, principalmente se o botão estiver dentro de um formulário, para evitar comportamentos automáticos indesejados.

Exemplo:

  • Dentro de um formulário: Se você não definir o tipo como button, o botão pode acabar enviando o formulário automaticamente (comportamento padrão do type="submit").
  • Com comportamento personalizado: Definir type="button" garante que o botão não faça nada por conta própria, permitindo que você defina claramente sua funcionalidade com JavaScript ou React.

Você pode sobrescrever atributos e estilos de um componente estilizado, utilizando o conceito de herança no styled-components e o uso da função .attrs para adicionar ou modificar atributos em componentes baseados em outros.

import React from 'react';
import styled from 'styled-components';

// Componente base: Input
const Input = styled.input.attrs<{ $size?: string }>((props) => ({
  type: 'text', // Atributo estático
  $size: props.$size || '1em', // Prop dinâmica com valor padrão
}))`
  border: 2px solid #bf4f74;
  margin: ${(props) => props.$size};
  padding: ${(props) => props.$size};
`;

// Componente PasswordInput que sobrescreve os atributos e estilos do Input
const PasswordInput = styled(Input).attrs({
  type: 'password', // Sobrescreve o tipo do Input para "password"
})`
  border: 2px solid aqua; // Sobrescreve o estilo de borda do Input

  /* Você também pode adicionar novos estilos específicos ao PasswordInput */
  background-color: #f0f0f0;
  font-size: 1.2em;
`;

const App = () => {
  return (
    <div>
      {/* Usando o Input base */}
      <Input placeholder="A bigger text input" $size="2em" />
      <br />
      {/* Usando o PasswordInput com atributos sobrescritos */}
      <PasswordInput placeholder="A bigger password input" $size="2em" />
    </div>
  );
};

export default App;

Animações

No CSS, animações são definidas usando a regra @keyframes, que permite criar efeitos de movimento ou transição para os elementos da página. No entanto, quando você define uma animação com @keyframes, ela não está restrita a um componente específico — ou seja, ela pode ser aplicada a qualquer elemento na página, o que pode gerar conflitos de nomes se houver animações com o mesmo nome.

O styled-components oferece o helper keyframes, que faz com que o nome da animação seria único para cada componente, garantindo que não haja conflito, mesmo que você use o mesmo nome:

// Crie os keyframes
const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

// Aqui criamos um componente que vai rotacionar tudo o que passarmos para 
// ele em dois segundos
const Rotate = styled.div`
  display: inline-block;
  animation: ${rotate} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`;

render(
  <Rotate>&lt; 💅🏾 &gt;</Rotate>
);

Temas

No styled-components, os temas permitem que você centralize e gerencie valores compartilhados, como cores, espaçamentos, fontes, e outros estilos que podem ser usados em toda a aplicação. Isso facilita a criação de uma interface consistente e a alternância entre diferentes temas, como um tema claro e um tema escuro.

styled-components: Advanced Usage

O ThemeProvider é o componente responsável por fornecer o tema para toda a sua aplicação. Você define o tema como um objeto, contendo as propriedades que deseja compartilhar.

export const lightTheme = {
  colors: {
    primary: '#6200ee',
    background: '#ffffff',
    text: '#000000',
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
};

export const darkTheme = {
  colors: {
    primary: '#bb86fc',
    background: '#121212',
    text: '#ffffff',
  },
  spacing: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
};

Para aplicar o tema com os valores configurados abaixo, você pode utilizar o ThemeProvider em volta da sua aplicação:

import React from 'react';
import { ThemeProvider } from 'styled-components';
import { lightTheme } from './theme';
import styled from 'styled-components';

const Container = styled.div`
  background-color: ${(props) => props.theme.colors.background};
  color: ${(props) => props.theme.colors.text};
  padding: ${(props) => props.theme.spacing.medium};
`;

function App() {
  return (
    <ThemeProvider theme={lightTheme}>
      <Container>
        Olá, styled-components com temas!
      </Container>
    </ThemeProvider>
  );
}

export default App;

Para alternar entre os temas, você pode controlar o valor passado ao ThemeProvider através de um estado no React:

import React, { useState } from 'react';
import { ThemeProvider } from 'styled-components';
import { lightTheme, darkTheme } from './theme';
import styled from 'styled-components';

const Container = styled.div`
  background-color: ${(props) => props.theme.colors.background};
  color: ${(props) => props.theme.colors.text};
  padding: ${(props) => props.theme.spacing.medium};
`;

const Button = styled.button`
  background: ${(props) => props.theme.colors.primary};
  color: ${(props) => props.theme.colors.text};
  border: none;
  padding: ${(props) => props.theme.spacing.small};
  margin-top: ${(props) => props.theme.spacing.medium};
  cursor: pointer;
`;

function App() {
  const [isDarkTheme, setIsDarkTheme] = useState(false);

  const toggleTheme = () => setIsDarkTheme(!isDarkTheme);

  return (
    <ThemeProvider theme={isDarkTheme ? darkTheme : lightTheme}>
      <Container>
        <h1>Alternância de Temas</h1>
        <Button onClick={toggleTheme}>
          {isDarkTheme ? 'Mudar para Tema Claro' : 'Mudar para Tema Escuro'}
        </Button>
      </Container>
    </ThemeProvider>
  );
}

export default App;

Function themes

Através do styled-components é possível criar temas dinâmicos ao passar uma função na propriedade theme para o ThemeProvider. Essa função tem acesso ao tema pai (fornecido por um ThemeProvider superior na árvore de componentes) e pode gerar um novo tema com base nele.

Essa funcionalidade é extremamente útil quando você deseja criar variações contextuais de um tema, como inverter as cores, ajustar propriedades, ou até mesmo combinar temas existentes.

Quando você passa uma função como o valor da propriedade theme no ThemeProvider, essa função recebe o tema atual fornecido pelo ThemeProvider mais acima na hierarquia de componentes. A partir disso, ela pode criar um novo tema.

import React from 'react';
import styled, { ThemeProvider } from 'styled-components';

const Button = styled.button`
  background: ${(props) => props.theme.background};
  color: ${(props) => props.theme.color};
  border: 2px solid ${(props) => props.theme.border};
  padding: 10px 20px;
  cursor: pointer;
`;

// Tema inicial
const defaultTheme = {
  background: 'white',
  color: 'black',
  border: 'black',
};

// Função que inverte as cores
const invertTheme = ({ color, background, border }) => ({
  background: color,
  color: background,
  border: border,
});

function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <div>
        <h1>Function Themes</h1>
        {/* Primeiro botão usa o tema padrão */}
        <Button>Botão Padrão</Button>
        
        {/* Segundo botão usa um tema invertido */}
        <ThemeProvider theme={invertTheme}>
          <Button>Botão Invertido</Button>
        </ThemeProvider>
      </div>
    </ThemeProvider>
  );
}

export default App;

Neste exemplo acima, nós temos a seguinte situação:

  • O ThemeProvider raiz passa o defaultTheme para todos os componentes filhos. O primeiro botão utiliza as cores padrão (background: white, color: black).
  • O segundo ThemeProvider encapsula apenas o segundo botão e passa uma função (invertTheme) para a propriedade theme.
  • O invertTheme recebe o tema atual do ThemeProvider pai (defaultTheme) e cria um novo tema invertido: trocando as cores de background e color.
  • O tema invertido não altera o tema global. Ele afeta apenas os componentes que estão dentro de seu próprio ThemeProvider.

Imagine que queremos adicionar transparência ao tema pai para criar um efeito sobreposto (como em modais):

import React from 'react';
import styled, { ThemeProvider } from 'styled-components';

const Container = styled.div`
  background: ${(props) => props.theme.background};
  color: ${(props) => props.theme.color};
  padding: 20px;
  text-align: center;
`;

// Tema padrão
const defaultTheme = {
  background: '#ffffff',
  color: '#333333',
};

// Função para adicionar transparência ao tema pai
const transparentTheme = (theme) => ({
  ...theme,
  background: 'rgba(0, 0, 0, 0.7)', // Sobrepõe o fundo com transparência
  color: '#ffffff', // Texto claro para contraste
});

function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <Container>
        <h1>Tema Principal</h1>
        <ThemeProvider theme={transparentTheme}>
          <Container>
            <h2>Tema com Transparência</h2>
          </Container>
        </ThemeProvider>
      </Container>
    </ThemeProvider>
  );
}

export default App;

O tema de transparência (transparentTheme) herda e modifica o tema padrão, criando um efeito visual diferente sem duplicar código. O componente interno usa rgba(0, 0, 0, 0.7) para um fundo semitransparente, enquanto mantém o estilo geral do tema pai.

Hook useTheme

O useTheme é um hook fornecido pelo styled-components para acessar o tema atual diretamente dentro de um componente funcional. Isso é útil para componentes que não têm acesso direto às props do tema.

import React from 'react';
import { ThemeProvider, useTheme } from 'styled-components';
import { lightTheme } from './theme';
import styled from 'styled-components';

const Container = styled.div`
  background-color: ${(props) => props.theme.colors.background};
  color: ${(props) => props.theme.colors.text};
  padding: ${(props) => props.theme.spacing.medium};
`;

const CustomButton = styled.button`
  background: ${(props) => props.theme.colors.primary};
  color: ${(props) => props.theme.colors.text};
  border: none;
  padding: ${(props) => props.theme.spacing.small};
  cursor: pointer;
`;

function ChildComponent() {
  const theme = useTheme();

  return (
    <div>
      <p>Cor de fundo atual: {theme.colors.background}</p>
      <CustomButton>Botão estilizado</CustomButton>
    </div>
  );
}

function App() {
  return (
    <ThemeProvider theme={lightTheme}>
      <Container>
        <h1>Hook useTheme</h1>
        <ChildComponent />
      </Container>
    </ThemeProvider>
  );
}

export default App;

Adicionando um tema específico à um componente

Você pode informar um tema diretamente para um componente. Essa abordagem pode ser útil em situações onde:

  • Não há um ThemeProvider disponível no contexto da aplicação, mas você ainda deseja usar o sistema de temas.
  • Você deseja sobrescrever o tema global (definido no ThemeProvider) em um componente específico.
// Tema customizado
const customTheme = {
  background: '#6200ee',
  color: '#ffffff',
};

...

<Button theme={customTheme}>Botão com tema customizado</Button>

Objetos de estilo

O styled-components suporta tanto a escrita de código CSS como strings, quanto um objeto JavaScript. Esse recurso é útil quando você tem objetos de estilo existentes e deseja migrar gradualmente para componentes estilizados.

// Objeto estático
const Box = styled.div<{ $background?: string; }>({
  background: '#BF4F74',
  height: '50px',
  width: '50px'
});

// Adaptando com base nas props
const PropsBox = styled.div(props => ({
  background: props.$background,
  height: '50px',
  width: '50px'
}));

render(
  <div>
    <Box />
    <PropsBox $background="blue" />
  </div>
);

Materiais para se aprofundar

Styled Components: To Use or Not to Use?

styled-components: FAQs

Referências

styled-components: Basics