Styled Attack! Component based design usando Styled-Components, Styled-System e Styled-Tools

Dev_logue
10 min readApr 22, 2021

--

Styled Attack @Dev_logue

Você já ouviu falar em CSS-in-JS? Component Based Design? Theme Based Design?

Se você nunca ouviu falar nesses conceitos, bem provavelmente você pode estar escrevendo linhas e mais linhas de código para transformar seus componentes adaptáveis visualmente com o uso de mil e uma condições para alterar uma propriedade aqui e outra ali que vai impactar no style dos seus elementos. Mas não estamos, nem vamos falar aqui sobre as propriedades do styled-components, o assunto aqui é o nosso squad, Styled Attack! Styled-components, Styled-system e Styled-tools. Esses “salvadores” vieram para te ajudar a nunca mais perder tempo estilizando e centralizando sua atenção em fazer uma variação visual em um botão, por exemplo, e dedicar toda a sua atenção a lógica deixando a parte de estilização mais fácil e prática.

Você até o presente momento estilizando componentes :)

Se você tem algum problema com a “estética” do uso de CSS inline e comparou ou vai comparar esse modelo de desenvolvimento com o mesmo, leia o meu artigo — CSS-in-JS & CSS inline são irmãos de crime?

O que é o Styled Attack?

styled attack (carinhosamente apelidado por mim dessa maneira), consiste no uso de um conjunto de bibliotecas baseadas em styled-components para desenvolver uma aplicação orientada a componentização (Component based design).

Por que eu deveria usar esse método?

Em resumo, você vai ganhar velocidade de desenvolvimento, escalabilidade em seu projeto e uma metodologia de desenvolvimento muito robusta que pode te atender desde o uso simples, com unidades de medidas padrão, a um sistema ou biblioteca de componentes com um uso complexo e intrínseco de propriedades, tokens, variações e estilização.

Mas, como nosso objetivo aqui é entender a fundo essa narrativa vamos a abordagem detalhada:

1 — Por ser baseado em um conceito de Component based design e theme based style você evita criar limitações, e conquista um numero infinito de propriedades para estilizar um componente sem precisar manualmente tratá-las.

2 — O uso do CSS-in-JS agiliza e facilita o desenvolvimento de componentes e templates na sua aplicação.

3 — A padronização de propriedades e o sistema de temas “obrigam” os desenvolvedores a não criarem propriedades de estilo confusas ou com falta de concisão.

4 — Melhora a comunicação e fluxo de trabalho entre Designer e Frontend com a possibilidade do uso de Tokens e Temas pré definidos.

Esses são apenas 4 pontos dos milhares e milhares que podemos abordar aqui nesse artigo, mas, para não nos estendermos demais, vamos logo para os exemplos!

Vamos a mágica!

O styled-system, nosso ranger amarelo, basicamente te dá a possibilidade de adicionar propriedades aos seus componentes de estilo. Naturalmente com o uso de apenas styled-components, nosso ranger vermelho, para criarmos uma propriedade CSS ficaria algo muito similar à:

import React from 'react';
import styled from 'styled-components';
const StyledDiv = styled.div`
background: ${props => props.bg};
`;
export default function teste() {
return <StyledDiv bg="red">Hello Dev_Logue!</StyledDiv>;
}

O que o styled-system faz é, nada mais nada menos, do que se aproveitar da possiblidade desse uso de “styled components” para passar propriedades ao seu componente. Logo, no styled-system, por trás dos panos, algo muito parecido com isso está acontecendo:

const background = (props) => {
if (props.background) {
return { background: props.background}
}
}
const Component = styled.div`
${background}
`
<Component background='red' />

Utilizando o styled-system que abstrai essa parte da criação desses “componentes estilizados temos o uso da própria documentação conforme o exemplo:

import { typography, space, color } from 'styled-system'

const Box = styled('div')(
typography,
space,
color
)
<Box
fontSize={4}
fontWeight='bold'
p={3}
color='white'
bg='primary'>
Hello
</Box>

Um dos grandes benefícios desse conceito é a possibilidade do uso de CSS-in-JS padronizado. Como o próprio Styled-system já se preocupou em padronizar essas propriedades e seus tipos, e até mesmo criou abreviações como por exemplo o uso de p abreviando o padding, nós não cometemos mais erros de concisão em projetos, como o exemplo a seguir:

<Componente corFundo="black"/>
<ComponenteDois darkTheme={true} />
<ComponenteTres horaDeMorfar />

Fundamentalmente, esses três componentes tem o mesmo intuito. Deixar a cor de fundo com uma cor escura. Porém, em grandes empresas, cada programador tende a pensar do seu próprio jeito, transformando o entendimento de propriedades de estilização quase um jogo de adivinhação.

Como facilitar a comunicação com Designers e desenvolver um projeto com theme based style?

Se você reparou atentamente ao exemplo acima, fontSize={4} não é uma unidade de medida padrão de CSS. Então, de onde saiu isso?

Uma das mágicas do styled-system é justamente a interpretação de tokens dentro do tema da nossa aplicação. Digamos que eu costume muito utilizar um tom de laranja em meu código. Logo, ao invés de eu precisar ficar escrevendo seu RGB ou Hex code eu posso no tema atribuir um “apelido” para aquele valor(token) e utilizá-lo através da interpretação do styled-system. Confira comigo no replay:

// theme.js
export default {
colors: {
laranjaDevlogue: '#c38a2e',
},
}
// no Componente
<Box backgroundColor="laranjaDevlogue" />

Não se esqueça que o tema deve ser servido através do ThemeProvider.

Dessa forma fica muuuito mais simples estilizarmos! Mas, como podemos usar isso para agilizar e melhorar a comunicação com os designers?

Levando em consideração que o styled-components conta com o ThemeProvider e o styled-system tem esses sistema de interpretação de tokens, agora nós podemos com o designer utilizarmos de valores padrões e apelidarmos os mesmos, logo, quando o layout for finalizado e publicado no figma por exemplo, os valores de cores não estarão em valores regulares como “red” ou “#000” e sim no formato de tokens. Esse processo irá te encaminhar a criação de um design system”.

https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens
https://www.figma.com/community/plugin/843461159747178978/Figma-Tokens

Feito isso, você nunca mais irá precisar brigar com o designer ou ficar horas ajustando “no olho” o layout para corresponder ao planejado pelo seu amigo. Você vai usar os tokens e já sempre, sempre mesmo, vai saber qual valor utilizar em box-shadow, paddings, margins, sizes, colors e qualquer outra propriedade de estilo sem ficar copiando e colando códigos extensos e confusos para interpretar o estilo reproduzido no figma. Dessa maneira, alterar proporcionalmente estilos e criar variações de tema fica mais fácil do que se molhar em dia de chuva.

Mas isso vai acabar criando mais trabalho para o designer!

Isso depende. O sistema de tokens pode ser desenvolvido como parte de um design system pela sua empresa ou designer dela. E isso realmente dá um trabalhão! Mas, se sua empresa tem um produto, isso já deveria estar no backlog de vocês. Um design system é o suprassumo da concisão de comunicação visual de uma empresa, evitando que cada squad desenvolva a seu bel prazer o layout.

Mas, se mesmo assim sua empresa não tem um design system, tokens nem a vontade de se criá-los, você pode tirar proveito de tokens já existentes. Um exemplo muito utilizado é o uso da base do tailwind CSS, ideia apresentada a mim pela primeira vez por um amigo, Vinicius Pacheco.

O tailwind CSS já conta com uma base de tokens e tema pré desenvolvida que você pode tirar proveito dela aplicando ao seu projeto. Essa base pode ser utilizada pelo seu designer por exemplo, sendo facilmente exportada do tailwind e importada no figma. E você, consequentemente, pode também implementar essa base de tokens e ambos(designer e frontend) se comunicarem na mesma “linguagem”.

Agora vamos a parte complexa! Como usar essa lógica de desenvolvimento.

Primeiramente, aqui vai um agradecimento especial ao Diogo Biz, foi com ele que tive primeiro contato com esse conceito e hoje, assim como ele, estou espalhando a palavra do Styled Attack.

A maneira como eu utilizo hoje em dia pode não ser idêntica a da maneira que me foi apresentada, mas dados os devidos ajustes do meu jeito de trabalhar, que você também deve fazê-los, eu vos apresento o método.

Basicamente nós iremos utilizar como base da nossa aplicação um componente chamado Box, igual ao exibido na documentação do Styled System.

import styled from 'styled-components';
import { compose, space, ... SpaceProps, ...} from 'styled-system';
export type BoxProps = SpaceProps & LayoutProps & ColorProps & FlexboxProps & BackgroundProps & BorderProps & PositionProps & ShadowProps & GridProps & TypographyProps;export const Box = styled.div<BoxProps>`
${compose( space, layout, color, flexbox, background, border, position, shadow, grid, typography )}
`;

Esse componente no nosso projeto deve se comportar como uma espécie de “coringa, qualquer componente que viermos a criar será baseado no “Box”. Com o setup realizado acima, nosso componente poderá receber qualquer propriedade relacionada a SpaceProps, LayoutProps… e assim por diante. Essas propriedades, alinhadas com o uso da prop “asfornecida pelo próprio Styled-Components nos permite utilizar o nosso componente de infinitas maneiras conforme o exemplo:

<Box as="p" p="3" bg="black" />
<Box mt="20px" color="customWhite" />
<Box as="img" src="..." alt="teste" borderRadius="20px" />
...

Dito isso você também pode criar se preferir um componente coringa adicional para tipografia conforme o exemplo a seguir:

import React, { useMemo } from 'react';
import styled from 'styled-components';
import { theme } from 'styled-tools';
import { typography, TypographyProps } from 'styled-system';
import Truncate, { TruncateProps } from 'react-truncate';
import { Box, BoxProps } from '../Box';
export type TextPropsStyled = BoxProps & TypographyProps;export type TextProps = TextPropsStyled & {
truncate?: TruncateProps;
as?: any;
htmlFor?: string;
} & React.AnchorHTMLAttributes<HTMLAnchorElement>;
export const TextStyled = styled(Box).attrs(props => ({
color: props.color || 'textGray'
}))<TextProps>`
${typography} ::selection {
background-color: ${theme('resume.textLightGray')};
}
`;
export const Text: React.FC<TextProps> = ({ children, truncate, ...props }) => {
const content = useMemo(() => {
if (!!truncate) {
return <Truncate {...truncate}>{children}</Truncate>;
}
return children;
}, [truncate]);
return <TextStyled {...props}>{content}</TextStyled>;
};

Se você observar bem, com o uso do styled attack podemos não só então definirmos componentes que recebem parâmetros baseados em estilização, como os mesmos também podem ser estendidos. No exemplo acima nós estamos estendendo o componente Box para criar o componente estilizado TextStyled.

Para mais informações sobre o uso do attrs consulte a documentação.

Mas desse jeito meu código fica muito verboso! Não gostei 😢

Sem problemas, realmente em alguns casos as configurações e propriedades ficam muito extensas, justamente por isso podemos solucionar esses casos de maneiras variadas, uma delas é com o uso do attrs abordado acima. Vamos falar a seguir de algumas outras formas de reorganizarmos para atender a todos os gostos.

Como vimos acima, podemos estender o nosso componente coringa (Box) e continuarmos utilizando suas propriedades no componente estendido:

...
import {Box} from "components/Box";
const Image = styled(Box)`
border-radius: 30px;
`
<Image src="..." p="20px" />

Se você achou verboso demais a estilização inline, e costuma organizar seu projeto conforme a forma abaixo, você poderá manter este modelo. Basicamente tudo o que você precisa fazer é ao invés de basear seus componentes em marcações html, baseá-los em nosso coringa Box.

components
- Panel
- index.ts
- Panel.tsx
- styles.tsx
...

Utilizando este método de extensão, você pode não só criar componentes baseados no Box como definir propriedades iniciais para o nosso componente conforme o exemplo abaixo:

import styled from 'styled-components'
import { Box } from 'components/Box'

const Card = styled(Box)`...`;

Card.defaultProps = {
p: 2,
bg: 'white',
}

export default Card

Usando os defaultProps do componente podemos definir um valor inicial de propriedade de estilização que pode ser facilmente sobrescrito ao ser passado um novo valor no momento do uso do componente. Abaixo vamos sobrescrever o backgroundColor padrão “white” do componente criado acima por um da cor vermelha no momento do uso:

<Card bg="red" />

Media Queries!

Se você até aqui curtiu o conceito mas não faz ideia de como se fazer as media queries, se liga que legal! Com esse combo incrível podemos criar media queries em propriedades utilizando de um array de propriedades no parâmetro, conforme o exemplo:

<Box m={[ 1, 2, 3, 4 ]} />

Mas dessa forma é bem confusa né? Então vamos melhorar isso um pouco para deixar mais intuitivo! Se liga no exemplo abaixo utilizando nossos tokens do theme:

// theme.js
const breakpoints = ['40em', '52em', '64em', '80em']

// aliases
breakpoints.sm = breakpoints[0]
breakpoints.md = breakpoints[1]
breakpoints.lg = breakpoints[2]
breakpoints.xl = breakpoints[3]

export default {
breakpoints,
}
<Box width={{ _: "100px", sm: "100px", md: "200px", lg: "500px" }} />

Agora com os breakpoints definidos no nosso theme e devidamente exportado e servido através do nosso provider podemos definir media queries inline com o uso de um objeto de configurações baseados nos nossos breakpoints.

Conclusão

Com esse método de criar seus componentes estilizados você consegue agilizar muito seu método de desenvolvimento e criar temas baseados em tokens e deixar a escalabilidade, ao menos por parte de estilização e customização de componentes, muito mais fluida. Existem ainda diversas outras funcionalidades que abordaremos em futuros artigos sobre como usar esse combo (styled attack) para criar variações de componentes, condições e várias outras funcionalidades incríveis! Mas como o artigo já está bem denso, vamos deixar o tutorial até aqui e em um futuro falamos com mais calma sobre esses outros exemplos.

--

--

Dev_logue
Dev_logue

Written by Dev_logue

💻 +10 anos Dev apaixonado❤ 👨🏼‍🏫 +4 anos lecionando para amantes de bugs

No responses yet