Atomic design no front-end: construa componentes escaláveis e modulares

Atomic design no front-end: construa componentes escaláveis e modulares

Lembra das aulas de química, quando a professora dizia que o átomo é a menor unidade da matéria?

Pois é, eu sempre fui mediano na disciplina de química na escola, mas entender como o átomo funciona e que a combinação deles formam moléculas e as moléculas formam organismos complexos foi fundamental para desenvolver aplicações front-end de maneira muito melhor.

E o que química tem a ver com desenvolvimento frontend? Tudo! A ideia do atomic design (Design Atômico) veio justamente dessa teoria: construir algo grande (uma aplicação) a partir de partes pequenas (os componentes).

Neste artigo você aprenderá:

  • O que é o atomic design e como ele funciona;
  • Quais são as estruturas principais: átomos, moléculas, organismos, templates e páginas;
  • Vantagens de aplicar o atomic design em projetos frontend;
  • Como o atomic design se conecta com design systems;

Então, vem comigo!

O que é atomic design?

De forma simples, atomic design é uma filosofia para organizar componentes de UI (User Interface, ou em tradução livre, interface do usuário) de forma hierárquica e lógica.

Essa metodologia foi desenvolvida em 2013 por Brad Frost, e a ideia dela é quebrar interfaces complexas em partes menores e mais simples e, a partir disso, montar interfaces mais complexas e completas.

Como o nome sugere, essa metodologia foi inspirada na química, onde um organismo é um conjunto de moléculas, e as moléculas compostas por dois ou mais átomos, sendo o átomo a menor parte desse sistema.

Se adaptarmos isso para uma aplicação front-end, e se em vez de termos componentes gigantes, nós dividíssemos tudo em partes menores e reutilizáveis?

O atomic design propõe justamente isso: quebrar a interface em pedaços menores, que podem ser combinados para criar qualquer tela da aplicação.

Então, se até o universo que é enoooorme é composto de partes menores como átomos, moléculas, e tudo o mais, você não vai querer deixar suas aplicações com componentes gigantes, né?

Então, vem comigo descobrir como aplicar o atomic design na prática nas suas aplicações.

Banner promocional da Alura, com um design futurista em tons de azul, apresentando o texto

Estrutura do atomic design

Imagine que você começou a trabalhar em um e-commerce que vende produtos geek, como canecas, xícaras, camisetas e muito mais. O site desse e-commerce é algo parecido com isso:

Interface de uma loja online com temática geek, exibindo produtos como camisetas, action figures e canecas organizados em um grid com preços e descrições curtas.

Essa aplicação foi criada com React.js e seu trabalho é criar novas features, telas, fazer a manutenção do código, etc. Mas ao olhar o projeto do site você se deparou com um único componente App.jsx com o seguinte código:

import './App.css'

const App = () => {

  return (
    <div class="app">
      <div class="sidebar">
        <h2>Loja Geek</h2>
        <ul>
          <li>Início</li>
          <li>Produtos</li>
          <li>Contato</li>
        </ul>
      </div>
      <div class="content">
        <div class="search-bar">
          <input type="text" placeholder="Buscar produtos..." />
          <button type="button">Pesquisar</button>
        </div>

        <div class="cards">
          <div class="card">
            <img src="https://i.pinimg.com/736x/94/4c/ce/944cceec5441301d7ef71bacbbd6b055.jpg" alt="Produto 1" />
            <h3>Camiseta Star Wars</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 59,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/736x/12/94/90/129490626e302c8b80928b7d768a25f5.jpg" alt="Produto 2" />
            <h3>Action Figure Thanos</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 199,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/58/a5/06/58a506e6c761ba31bc69c0d5904d329d.jpg" alt="Produto 3" />
            <h3>Caneca Senhor dos Anéis</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 39,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/a1/df/2c/a1df2ca3406a0ab8be69b1e2c2467c5e.jpg" alt="Produto 4" />
            <h3>Poster Vingadores</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 29,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/b3/e8/47/b3e84763179a2d9fe8957eeaed5fb5ed.jpg" alt="Produto 5" />
            <h3>Camiseta Geek</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 49,90</span>
          </div>
          <div class="card">
            <img src="  https://i.pinimg.com/564x/f6/e4/65/f6e46510b63159fb13c12450cdfe7bb9.jpg" alt="Produto 6" />
            <h3>Fone de Ouvido Gamer</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 299,90</span>
          </div>
        </div>
      </div>
    </div>
  )
}

export default App

Aposto que sua reação seria algo parecido com isso:

Gif de um cachorro com uma expressão assustada e confusa sem entender o que está acontecendo.

O problema de um componente gigante como este é que não conseguimos reutilizar a barra de pesquisa, ou o botão de buscar e até mesmo a sidebar em outra tela da aplicação.

Teríamos que duplicar o código e isso não é uma boa prática. Então, a primeira tarefa que você irá fazer é melhorar este código.

Vamos começar quebrando o App.jsx em componentes menores e entender as principais estruturas do atomic design.

Átomos

Os átomos são os elementos mais básicos da interface. Pense em botões, inputs, labels, cores e tipografia.

São independentes e não fazem muito sozinhos, mas são fundamentais. Então, da aplicação Geek Store poderíamos começar separando aquelas partes que podem ser átomos. Acompanha comigo:

  • Botão
const Button = ({ children, onClick }) => (
  <button onClick={onClick} className="button">
    {children}
  </button>
);
  • Campo de Texto
const Input = ({ placeholder, onChange }) => (
  <input
    type="text"
    placeholder={placeholder}
    onChange={onChange}
    className="input"
  />
);
  • Imagem do produto
const ProductImage = ({ src, alt }) => (
  <img src={src} alt={alt} className="product-image" />
);

Um botão é um botão em qualquer lugar. Da mesma forma um input. Então, você pode ter botões diferentes na aplicação, e tudo bem.

Para isso, pode misturar técnicas de composição para criar variações desses elementos quando fizer sentido.

Moléculas

Quando combinamos átomos, criamos moléculas. Por exemplo, um input com um label e um botão para envio.

Então, podemos combinar alguns dos átomos que criamos anteriormente para criar moléculas na nossa aplicação

  • Barra de busca
const SearchBar = ({ placeholder, onSearch }) => {
  const handleSearch = () => onSearch();

  return (
    <div className="search-bar">
      <Input placeholder={placeholder} />
      <Button onClick={Search}>Pesquisar</Button>
    </div>
  );
};

Combinamos aqui o campo de texto e o botão, ambos átomos.

  • Informação do produto
const ProductInfo = ({ title, description, price }) => (
  <div className="product-info">
    <h3>{title}</h3>
    <p>{description}</p>
    <span>{price}</span>
  </div>
);

No card de produto, usamos tags de h3, pe span. Cada um desses elementos poderia ser um único componente de Tipografia, pois eles não podem ser compostos de outros. Eles são indivisíveis, e portanto podem ser átomos na nossa aplicação.

const Typography = ({ variant, children, classname }) => {
  const Tag = variant; // Define a tag dinamicamente com base na variante

  return <Tag className={classname}>{children}</Tag>;
};

export default Typography;

E agora o componente de ProductInfo pode ser escrito desta forma:

const ProductInfo = ({ title, description, price }) => (
  <div className="product-info">
    <Typography variant="h3" classname="product-title">
      {title}
    </Typography>
    <Typography variant="p" classname="product-description">
      {description}
    </Typography>
    <Typography variant="span" classname="product-price">
      {price}
    </Typography>
  </div>
);

Repara que tínhamos uma molécula e dividimos ela em átomos? Neste caso, só um átomo de tipografia que serve para exibir vários estilos de textos diferentes.

No mundo real não tente dividir os átomos. Isso pode acontecer:

Gif do personagem Coiote do desenho animado Looney Tunes tentando acender uma bomba enquanto instantaneamente tudo explode no cenário, inclusive ele mesmo.

Organismos

Os organismos são conjuntos de moléculas que formam uma seção funcional da interface, como um header ou uma sidebar. Então, na aplicação podemos ter:

  • Card do produto
const ProductCard = ({ product }) => (
  <div className="card">
    <ProductImage src={product.image} alt={product.title} />
    <ProductInfo
      title={product.title}
      description={product.description}
      price={product.price}
    />
  </div>
);

Aqui combinamos um átomo (ProductImage) e uma molécula (ProductInfo) para formar o ProductCard.

  • Lista de cards
const ProductList = ({ products }) => (
  <div className="cards">
    {products.map((product) => (
      <ProductCard key={product.id} product={product} />
    ))}
  </div>
);

Já neste componente, estamos usando um organismo para compor outro, pois não faria sentido usar átomos e moléculas menores, já que já temos um organismo que encaixa perfeitamente aqui.

  • Barra Lateral
const Sidebar = () => (
  <div className="sidebar">
    <h2>Loja Geek</h2>
    <ul>
      <li>Início</li>
      <li>Produtos</li>
      <li>Contato</li>
    </ul>
  </div>
);

Você pode tá pensando que essa sidebar não é um organismo, pois ela não é composta de moléculas, ou átomos e moléculas. Tem certeza?

Repara bem no título, ele não poderia ser um componente de tipografia? E essa ul? Se trocássemos a ul por um nav, não seria um menu de navegação, com links e tudo mais? Então, sim. A Sidebar pode ser um organismo se escrevêssemos ela assim:

const Sidebar = () => (
  <div className="sidebar">
    <Typography variant="h2" classname="sidebar-title">
      Loja Geek
    </Typography>
    <Menu>
      <MenuItem>Início</MenuItem>
      <MenuItem>Produtos</MenuItem>
      <MenuItem>Contato</MenuItem>
    </Menu>
  </div>
);

export default Sidebar;

Onde o Menu e o MenuItem são átomos/moléculas, dependendo de como é a sua aplicação.

Templates

Templates são esqueletos de página. Eles definem a estrutura geral sem amarrar conteúdo específico.

Essa estrutura é muito fácil de confundir com as páginas, mas pensa no template como a estrutura do que você quer construir. Vamos a um exemplo no site da Geek Store:

  • Estrutura principal
const MainLayout = ({ children }) => (
  <div className="app">
    <Sidebar />
    <div className="content">{children}</div>
  </div>
);

Esse componente combina a barra lateral (organismo) e algum conteúdo.

  • Conteúdo principal
const Content = ({ products }) => (
  <>
    <SearchBar placeholder="Buscar produtos..." />
    <ProductList products={products} />
  </>
);

Já este componente é o conteúdo em si, ele combina a barra de pesquisa (molécula) e a lista de produtos (organismo).

Viu como combinamos várias estruturas diferentes que falamos anteriormente para construir o template?

Essa é sacada da metodologia do design atômico. A gente tem um grande poder de reutilização dos componentes criados segundo essa metodologia.

Páginas

As páginas são versões específicas dos templates, com o conteúdo final. Um esqueleto sozinho não faz nada, certo? Ele não dá vida a um corpo, a menos que seja em algum filme de zumbis (risos).

Um corpo precisa do esqueleto e de músculos, tendões, pele, etc. As páginas são esqueletos com tudo isso.

Elas já são a versão da sua aplicação que as pessoas verão quando acessarem ela. Na nossa página, poderia ser algo do tipo:

const EcommercePage = () => {
  const products = [
    {
      id: 1,
      image:
        "https://i.pinimg.com/736x/94/4c/ce/944cceec5441301d7ef71bacbbd6b055.jpg",
      title: "Camiseta Star Wars",
      description: "Descrição curta do produto.",
      price: "R$ 59,90",
    },
    // Outros produtos...
  ];

  return (
    <MainLayout>
      <Content products={products} />
    </MainLayout>
  );
};

export default EcommercePage;

Agora se olharmos o código do App.jsx lá do começo do artigo e comparar o que tínhamos antes com o componente EcommercePage, aquele monstro gigante foi reduzido a três linhas de código. Dá uma olhada no código abaixo:

import './App.css'

const App = () => {

  return (
    <div class="app">
      <div class="sidebar">
        <h2>Loja Geek</h2>
        <ul>
          <li>Início</li>
          <li>Produtos</li>
          <li>Contato</li>
        </ul>
      </div>
      <div class="content">
        <div class="search-bar">
          <input type="text" placeholder="Buscar produtos..." />
          <button type="button">Pesquisar</button>
        </div>

        <div class="cards">
          <div class="card">
            <img src="https://i.pinimg.com/736x/94/4c/ce/944cceec5441301d7ef71bacbbd6b055.jpg" alt="Produto 1" />
            <h3>Camiseta Star Wars</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 59,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/736x/12/94/90/129490626e302c8b80928b7d768a25f5.jpg" alt="Produto 2" />
            <h3>Action Figure Thanos</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 199,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/58/a5/06/58a506e6c761ba31bc69c0d5904d329d.jpg" alt="Produto 3" />
            <h3>Caneca Senhor dos Anéis</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 39,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/a1/df/2c/a1df2ca3406a0ab8be69b1e2c2467c5e.jpg" alt="Produto 4" />
            <h3>Poster Vingadores</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 29,90</span>
          </div>
          <div class="card">
            <img src="https://i.pinimg.com/564x/b3/e8/47/b3e84763179a2d9fe8957eeaed5fb5ed.jpg" alt="Produto 5" />
            <h3>Camiseta Geek</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 49,90</span>
          </div>
          <div class="card">
            <img src="  https://i.pinimg.com/564x/f6/e4/65/f6e46510b63159fb13c12450cdfe7bb9.jpg" alt="Produto 6" />
            <h3>Fone de Ouvido Gamer</h3>
            <p>Descrição curta do produto.</p>
            <span>R$ 299,90</span>
          </div>
        </div>
      </div>
    </div>
  )
}

export default App

E sabe o que é melhor? Todos os outros componentes que criamos podem ser reutilizados em quaisquer outros componentes da nossa aplicação, desde que fizerem sentido.

Maneiro, né? Essa é uma das vantagens do design atômico. Vem comigo conhecer duas outras.

Vantagens do atomic design

Acredito que você já deve ter percebido as vantagens do atomic design para o projeto, mas vou listar aqui embaixo as principais:

  • Reutilização: um botão é um botão em qualquer lugar. Então, conseguimos menos duplicação, mais DRY (Don't Repeat Yourself);
  • Escalabilidade: projetos grandes ficam mais organizados, cada componente com sua própria responsabilidade;
  • Manutenção: quer alterar a cor de um botão ou adicionar uma nova variante? Mude o átomo e pronto, tudo se ajusta.

Atomic design e design systems

E se você está se perguntando pra quê precisa saber e aplicar o design atômico em seus projetos, um dos principais motivos são os Designs Systems, conceito muito importante no design mas que é fundamental toda pessoa desenvolvedora conhecer.

Os design systems garantem que as equipes de design e desenvolvimento estejam alinhadas, promovendo uma experiência do(a) usuário(a) equilibrada em todo o produto.

Quer entender mais sobre desing system e como eles são aplicados em equipes de desenvolvimento, dá só uma olhada neste episódio do Hipsters.tech.

O atomic design é estruturado de forma modular, desde átomos até organismos e templates mais complexos, facilitando o reaproveitamento de componentes.

Essa abordagem também simplifica a manutenção: ao atualizar um átomo, todas as partes que o utilizam são automaticamente ajustadas.

Ferramentas como Storybook complementam essa metodologia, ajudando na documentação e visualização, reduzindo retrabalho e tornando o desenvolvimento mais produtivo.

Se você quiser aprender como criar um design system usando o Next.js e o TailwindCSS, além de documentar tudo com o Storybook, recomendo dar uma olhada nesta formação.

Conclusão

O atomic design é uma abordagem prática que não só te ajuda a criar componentes reutilizáveis e escaláveis como também facilita a comunicação entre os times de design e desenvolvimento, permitindo com que seu produto seja desenvolvido de forma muito mais produtiva.

E se você quiser se aprofundar no tema e construir design systems incríveis aplicando o atomic design vou deixar algumas das principais conteúdos e formações que temos aqui:

Referências

Neilton Seguins
Neilton Seguins

Sou graduado como Bacharel em Ciência e Tecnologia e em Engenharia Mecânica. Atuo como Instrutor de Desenvolvedor de Software na Alura e possuo experiência com desenvolvimento usando JavaScript/TypeScript, React js, Next js e Node.js. Amo compartilhar conhecimento, pois acredito que a educação é transformadora e quero mudar a vida de pessoas através da educação assim como consegui mudar de vida. Também amo ouvir e tocar música, ler livros e mangás e assistir séries.

Veja outros artigos sobre Front-end