Biblioteca de padrões primeiro: uma abordagem para gerenciar CSS
Publicados: 2022-03-10Neste artigo, baseado na palestra que dei na Smashing Conference em Toronto, vou descrever um método de trabalho que adotei nos últimos dois anos que me ajuda a gerenciar CSS em meus projetos.
Mostrarei como usar a ferramenta de biblioteca de padrões Fractal, para gerenciar seu CSS componente por componente, enquanto permite que você use as ferramentas com as quais já está familiarizado. Embora isso sirva como uma introdução ao Fractal e por que selecionamos essa biblioteca de padrões específica, é provável que essa maneira de trabalhar seja transferida para outras soluções.
Nossos projetos
Minha empresa tem alguns produtos - Perch e Perch Runway CMS e Notist, um software como um aplicativo de serviço para oradores públicos. Esses produtos são bem diferentes, especialmente porque o Perch é um sistema auto-hospedado e o Notist é SaaS, no entanto, ambos têm muita interface de usuário para desenvolver. Também temos todos os sites e documentação associados a esses produtos, além de outras coisas em que trabalhamos, como o site da 24 Ways. Depois de descobrir o Fractal há dois anos, movemos todos os novos projetos - grandes e pequenos - para o Fractal.
Problemas que queríamos resolver
Comecei a investigar soluções de biblioteca de padrões há dois anos, quando comecei a reconstruir a interface do usuário do Perch para a versão 3. Um recurso do Perch é que os modelos que você cria para a saída de conteúdo em seu site se tornam o esquema da interface do usuário do administrador. Isso significa que qualquer tipo de campo usado em um modelo precisa existir ao lado de qualquer outro tipo de campo. Não sabemos como nossos clientes podem combiná-los, e há um grande número de combinações possíveis. Também não é um "site" e eu não queria tentar forçar a biblioteca de padrões em algo projetado para organizar padrões de sites.
Como o Perch é auto-hospedado - as pessoas o baixam e o hospedam em seus próprios servidores - precisamos usar a pilha de tecnologia mais simples possível para não colocar barreiras adicionais à entrada das pessoas, muitas das quais são novas no uso de um CMS. Para adicionar um nível extra de diversão, oferecemos suporte de volta ao Internet Explorer 9, mas eu pretendia usar muito Flexbox - já que isso era antes do lançamento do Grid Layout.
Eu também queria evitar o uso de ferramentas que vinham com muito reaprender como trabalhávamos e mudar completamente nosso processo. Qualquer ferramenta adicional ou mudança na maneira como você trabalha em seus projetos traz novos atritos. Você pode resolver um problema, mas trazer um novo conjunto de problemas se fizer grandes mudanças na maneira como trabalha. No nosso caso, estávamos usando o Sass de uma maneira bastante limitada e processando isso usando o Gulp. Nenhum de nossos projetos usa um framework Javascript, estamos apenas escrevendo HTML, CSS e JavaScript.
Fractal se encaixa perfeitamente às nossas necessidades. É agnóstico quanto à maneira como você desenvolve ou às ferramentas que deseja usar. Importante para nossos propósitos, ele não assumiu que estávamos construindo um site. O experimento foi tão bem-sucedido que nos encontramos usando o Fractal para todos os projetos grandes ou pequenos, pois torna o processo de trabalhar em CSS muito mais simples. Mesmo pequenos sites que crio por conta própria geralmente começam a vida no Fractal, porque há mais benefícios do que você imagina em termos de trabalhar com uma biblioteca de padrões, e muitos desses benefícios fazem tanto sentido para uma equipe de um quanto para uma equipe grande .
Antes de pensarmos em como desenvolver usando o Fractal e por que acho que faz sentido para projetos pequenos e grandes, vamos dar uma olhada em como configurar o ambiente.
Começando com o Fractal
A abordagem mais direta para trabalhar com o Fractal é acessar o site do Fractal e dar uma olhada no Guia de Introdução. Primeiro você precisará instalar o Fractal globalmente, então você pode seguir os passos listados aqui para criar um novo projeto Fractal.
Com seu novo projeto instalado, na linha de comando mude para a pasta que você acabou de criar e execute o comando:
fractal start --sync
Isso iniciará um pequeno servidor na porta 3000, portanto, você poderá acessar https://localhost:3000
em um navegador da Web e ver seu projeto.
Agora que seu projeto está funcionando, abra a pasta do projeto em seu editor de texto favorito e encontre o componente de exemplo em components/example
. Você encontrará um arquivo de configuração e um arquivo chamado example.hbs . O template example.hbs é o HTML do seu componente, você pode adicionar um pouco mais de HTML a ele e o Fractal irá recarregar automaticamente e exibi-lo. Altere o arquivo para:
<h1>This is my heading</h1> <p>{{ text }}</p>
Você deve ver o título aparecer no navegador. O arquivo de configuração pode ser usado para adicionar conteúdo e configurar seu componente. Se você quiser ler o texto do cabeçalho desse arquivo, edite esse arquivo para se parecer com o seguinte exemplo:
title: Example component context: text: This is an example component! heading: My heading
Agora altere seu arquivo example.hbs para ler esse texto.
<h1>{{ heading }}</h1> <p>{{ text }}</p>
Adicionando Componentes Adicionais
Você pode seguir o padrão do componente de exemplo para adicionar o seu próprio. No mínimo, você precisa de uma pasta (o nome do componente) e um arquivo .hbs com o mesmo nome. Você pode adicionar o arquivo de configuração se quiser definir as opções de configuração.
Os componentes podem ser aninhados em pastas para facilitar a localização de componentes específicos, e como você estrutura as pastas depende completamente de você.
Nota : É muito fácil encontrar-se gastando muito tempo se preocupando com como nomear seus componentes. No Fractal, pelo menos, renomear e também reorganizar componentes em pastas é simples. Você pode renomeá-los ou movê-los e o Fractal será atualizado para mostrar a nova estrutura. Acho que muitas vezes a estrutura ideal só se torna aparente à medida que estou desenvolvendo, então não me preocupo muito no início e depois a firmo mais tarde.
Adicionando um fluxo de trabalho CSS
Até agora, conseguimos criar componentes HTML como templates de guidão e um arquivo de configuração para inserir dados, porém, não adicionamos nenhum CSS. Idealmente, queremos adicionar o CSS para cada componente na mesma pasta que o restante dos arquivos do componente e, em seguida, combiná-los todos.
Mencionei que o Fractal faz muito poucas suposições sobre seu fluxo de trabalho; devido a isso, ele faz muito menos fora da caixa do que poderia se estivesse forçando você a seguir uma maneira específica de trabalhar. No entanto, podemos facilmente fazer o Fractal funcionar com uma configuração do Gulp.
Combinando Fractal, Sass e Gulp
O seguinte descreve uma configuração mínima usando Gulp e Sass para criar um único arquivo CSS de saída. Espero que você possa seguir este processo para fazer qualquer outra coisa que normalmente faria no Gulp. A principal coisa a notar é que a maior parte disso não é específico do Fractal, então, uma vez que você tenha a parte Fractal funcionando, você pode adicionar qualquer outra coisa seguindo os mesmos padrões. Se você estiver familiarizado com outra ferramenta de compilação, é provável que possa criar um processo semelhante; se você fizer, e estiver feliz em compartilhar, deixe-nos saber nos comentários.
Primeiro alguma configuração, o seguinte permitirá que você acompanhe o código listado neste tutorial, os locais de seus arquivos Sass e CSS de saída podem ser diferentes dos meus. O principal é que o arquivo CSS de saída precisa estar em algum lugar na pasta pública.
- Dentro da pasta pública em sua instalação do Fractal, adicione uma pasta chamada css .
- Na pasta raiz do seu Fractal, instale adicione uma pasta assets dentro da qual está uma pasta scss . Crie um arquivo Sass chamado global.scss dentro dessa pasta. Dentro desse arquivo adicione a seguinte linha:
@import "../../components/**/*.scss";
- Crie um arquivo chamado example.scss em seu diretório de componente de
example
. - Crie gulpfile.js na raiz do seu projeto Fractal e adicione o código abaixo.
'use strict'; const gulp = require('gulp'); const fractal = require('./fractal.js'); const logger = fractal.cli.console; const sass = require('gulp-sass'); const sassGlob = require('gulp-sass-glob'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); const path = require('path'); gulp.task('sass',function() { return gulp.src('assets/scss/**/*.scss') .pipe(customPlumber('Error running Sass')) .pipe(sassGlob()) .pipe(sass()) .pipe(gulp.dest('public/css')) }); gulp.task('watch', ['sass'], function() { gulp.watch([ 'components/**/*.scss', 'assets/scss/**/*.scss' ], ['sass']); }); function customPlumber(errTitle) { return plumber({ errorHandler: notify.onError({ title: errTitle || "Error running Gulp", message: "Error: <%= error.message %>", }) }); } gulp.task('fractal:start', function(){ const server = fractal.web.server({ sync: true }); server.on('error', err => logger.error(err.message)); return server.start().then(() => { logger.success(`Fractal server is now running at ${server.url}`); }); }); gulp.task('default', ['fractal:start', 'sass', 'watch']);
Em seguida, instalo as dependências listadas na parte superior do arquivo. Se você fosse instalá-los na linha de comando, você executaria:
npm install gulp gulp-sass gulp-sass-glob gulp-plumber gulp-notify
A função sass
compila o Sass dos ativos em um único arquivo e o envia para a pasta em public
.
gulp.task('sass',function() { return gulp.src('src/assets/scss/**/*.scss') .pipe(customPlumber('Error running Sass')) .pipe(sassGlob()) .pipe(sass()) .pipe(gulp.dest('public/css')) });
Em seguida, crio uma função de watch
que observará meu Sass em assets
e também em componentes individuais e compilei na pasta pública.
gulp.task('watch', ['sass'], function() { gulp.watch([ 'components/**/*.scss', 'assets/scss/**/*.scss' ], ['sass']); });
Esse é o meu edifício CSS. Agora eu quero fazer isso para que eu possa executar gulp e ele iniciará a observação do arquivo CSS, bem como iniciará o fractal. Eu faço isso criando uma tarefa gulp para executar o comando fractal start.
gulp.task('fractal:start', function(){ const server = fractal.web.server({ sync: true }); server.on('error', err => logger.error(err.message)); return server.start().then(() => { logger.success(Fractal server is now running at ${server.url}); }); });
Por fim, preciso garantir que o edifício Sass e o Fractal iniciem quando eu executo o gulp e a linha de comando:
gulp.task('default', 'fractal:start', 'sass', 'watch');
Esse é o meu gulpfile.js completo. Se você adicionar isso ao seu projeto Fractal padrão, certifique-se de que as pastas estejam no lugar para os caminhos mencionados. Você deve poder ir para a linha de comando, executar gulp
e o Fractal será iniciado.
Podemos testar nosso Sass adicionando uma variável no arquivo global.scss ; você precisará adicionar isso acima da linha que inclui os componentes para que a variável esteja disponível para esses componentes.
$color1: rebeccapurple;
Então, em example.scss
, adicione uma regra para o título de nível 1 que adicionamos anteriormente:
h1 { color: $color1; }
Se tudo estiver configurado corretamente, você deve descobrir que tem um arquivo .css em public/css que contém a regra:
h1 { color: rebeccapurple; }
Precisamos fazer mais uma coisa para que possamos visualizar nossos componentes usando o CSS que estamos construindo. Precisamos criar um arquivo de visualização, que será vinculado na folha de estilo da pasta pública.
Dentro da pasta de componentes, crie um arquivo chamado _preview.hbs .
O arquivo de visualização é essencialmente um documento HTML, com links em nosso CSS e qualquer outra coisa que você precise incluir. No corpo há uma tag {{ yield }}
, e é aqui que um componente será colocado.
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Preview Layout</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="{{ path '/css/global.css' }}"> </head> <body> {{{ yield }}} </body> </html>
Observação : a pasta pública também pode abrigar quaisquer outros ativos que você precise exibir em componentes, como imagens, fontes e assim por diante.
A biblioteca de padrões como fonte de verdade
Como vimos, o Fractal pode construir nosso CSS. Em nossos projetos, fazemos com que o Fractal seja o único lugar onde construímos e processamos CSS e outros ativos para o site. O que isso significa é que nossa biblioteca de padrões e site ou aplicativo não se desviam. A deriva acontece depois que você implanta o site se as pessoas começarem a editar o CSS do site e não trazer essas alterações de volta para a biblioteca de padrões. Se você puder tornar a biblioteca de padrões o local onde o CSS é processado, as alterações terão que começar por aí - o que evita o deslocamento entre o site ativo e a biblioteca.
Construímos tudo no Fractal e depois copiamos esses ativos públicos para os sites ativos a serem implantados. Além de evitar desvios entre os sistemas, também facilita muito o gerenciamento de CSS no controle de origem. Quando várias pessoas trabalham em um arquivo CSS, os conflitos de mesclagem podem ser razoavelmente difíceis de lidar. Com pessoas trabalhando em componentes individuais na biblioteca de padrões, geralmente você pode evitar que duas pessoas façam alterações no mesmo arquivo ao mesmo tempo e, se o fizerem, será apenas um pequeno arquivo para classificar e não todo o seu CSS.
Usando uma primeira abordagem de biblioteca de padrões para gerenciar fallbacks
Descobri que a biblioteca de padrões de trabalho primeiro torna o tratamento de fallbacks em seu código muito mais direto e menos esmagador do que tentar consertar um site ou aplicativo completo de uma só vez. Também nos permite concentrar no melhor caso possível e ser criativos com novas técnicas, em vez de limitar o que fazemos devido à preocupação em como faremos com que funcione bem em navegadores não compatíveis.
Podemos examinar um caso simples de um componente de objeto de mídia para ver como isso pode funcionar. Para acompanhar, crie uma pasta de mídia dentro dos componentes no Fractal e adicione os arquivos media.hbs e media.scss .
Comece com uma boa marcação
Seu ponto de partida deve ser sempre uma marcação bem estruturada. Na biblioteca de padrões, pode ser que você use este componente com um intervalo de marcação, por exemplo, você pode usar um componente com conteúdo marcado como figura em um lugar e apenas com divs em outro. Seu conteúdo deve, no entanto, ser estruturado de forma que faça sentido e possa ser lido de cima para baixo.
Isso garante que seu conteúdo seja acessível em um nível muito básico, mas também significa que você pode aproveitar o fluxo normal. Fluxo normal é como os navegadores exibem seu conteúdo por padrão, com elementos de bloco progredindo um após o outro na dimensão do bloco e elementos embutidos — como palavras em uma frase — correndo ao longo do eixo embutido. Para um monte de conteúdo que é exatamente o que você quer, e aproveitando o fluxo normal em vez de lutar contra ele, você torna seu trabalho muito mais fácil ao criar seu layout.
Portanto, meu componente tem a seguinte marcação que adiciono a media.hbs .
<div class="media"> <div class="img"> <img src="/img/placeholder.jpg" alt="Placeholder"> </div> <h2 class="title">This is my title</h2> <div class="content"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis vehicula vitae ligula sit amet maximus. Nunc auctor neque ipsum, ac porttitor elit lobortis ac. Vivamus ultrices sodales tellus et aliquam. Pellentesque porta sit amet nulla vitae luctus. Praesent quis risus id dolor venenatis condimentum.</p> </div> <div class="footer"> An optional footer goes here. </div> </div>
Você pode ver como isso é exibido dentro do Fractal:
Assim que tiver a marcação que quero, vou trabalhar na tela da área de trabalho que tenho em mente. Vou usar CSS Grid Layout e o método grid-template-areas
para fazer isso. Adicione o seguinte a media.scss .
img { max-width: 100%; } .media > .title { grid-area: title; } .media > .img { grid-area: img; } .media > .content { grid-area: bd; } .media > .footer { grid-area: ft; } .media { margin-bottom: 2em; display: grid; grid-column-gap: 20px; grid-template-columns: 200px 3fr; grid-template-areas: "img title" "img bd" "img ft"; }
Agora temos um layout de objeto de mídia simples:
Algo que você pode fazer no Fractal é adicionar variações de um componente. Você pode querer virar o objeto de mídia para que a imagem fique à direita.
Agora adicione o CSS ao media.scss para inverter o layout:
.media.media-flip { grid-template-columns: 3fr 200px ; grid-template-areas: "title img" "bd img" "ft img"; }
Há duas maneiras de criar variantes: com base em arquivo e com base em configuração. Baseado em arquivo é mais simples e também é útil se sua variante tiver marcação diferente. Para criar uma variante baseada em arquivo, faça uma cópia do seu componente na pasta media com o nome media --flip.hbs (que são dois traços no nome do arquivo).
Esse componente deve ter marcação idêntica com a classe media-flip
adicionada à primeira linha, e você poderá ver as duas versões.
<div class="media media-flip">
Alternativamente, como neste caso tudo o que precisamos fazer é adicionar uma classe, você pode criar uma variante usando um arquivo de configuração.
Se você quiser fazer isso, remova seu arquivo variante e, em vez disso, adicione um arquivo de configuração chamado media.config.json contendo o seguinte código:
{ "title": "Media Object", "context": { "modifier": "default" }, "variants": [ { "name": "Flipped", "context": { "modifier": "flip" } } ] }
Em seguida, modifique a primeira linha de media.hbs da seguinte forma:
<div class="media media-{{ modifier }}">
Nota : Você pode adicionar quantas variantes desejar (veja a documentação de variantes para ler mais).
Podemos agora pensar em adicionar algum CSS para alterar o layout com base no tamanho da tela. Envolvendo o layout que criamos em uma consulta de mídia e, acima disso, criando um layout de coluna única para dispositivos menores.
img { max-width: 100%; } .media > .title { grid-area: title; } .media > .img { grid-area: img; } .media > .content { grid-area: bd; } .media > .footer { grid-area: ft; } .media { display: grid; grid-column-gap: 20px; grid-template-areas: "title" "img" "bd" "ft"; } @media (min-width: 600px) { .media { margin-bottom: 2em; display: grid; grid-column-gap: 20px; grid-template-columns: 200px 3fr; grid-template-areas: "img title" "img bd" "img ft"; } .media.media-flip { grid-template-columns: 3fr 200px ; grid-template-areas: "title img" "bd img" "ft img"; } }
Então, assim como gerenciamos a visualização para dispositivos menores em nosso componente, podemos gerenciar o layout para navegadores mais antigos que não suportam grade.
Nesse caso, vou construir um fallback baseado em float (isso funcionará para praticamente qualquer navegador legado). Só vou me preocupar com isso para tamanhos de tela maiores e deixar o componente sendo exibido em fluxo normal para dispositivos móveis mais antigos.
Apenas dentro da consulta de mídia, adicione o seguinte CSS:
.media:after { content: ""; display: table; clear: both; } .media > .media { margin-left: 160px; clear: both; } .media .img { float: left; margin: 0 10px 0 0; width: 150px; } .media.media-flip .img { float: right; margin: 0 0 0 10px; } .media > * { margin: 0 0 0 160px; } .media.media-flip > * { margin: 0 160px 0 0; }
Isso deve resolver a exibição em navegadores sem grade. Para navegadores que suportam grid, você não precisa se preocupar com floats, ou seja, quando o item float se torna um item de grid, o float é removido. O que será um problema são quaisquer margens. O layout em navegadores com suporte a grade agora será todo espaçado devido às margens extras.
É aqui que podemos adicionar uma consulta de recurso, removendo as margens se soubermos que nosso navegador suporta grid.
@supports(display: grid) { .media > *, .media.media-flip > * { margin: 0; } .media .img, .media.media-flip .img { width: auto; margin: 0; } .media:after { content: none; } }
Esse é o nosso pequeno componente finalizado. Embora seja um exemplo simples - e pode-se argumentar que é um que realmente não precisa de grade se você precisar ter um fallback - ele demonstra a abordagem que estou adotando em todos os meus projetos, grandes e pequenos.
Para colocar meu arquivo CSS em produção, podemos pegar o arquivo CSS da pasta pública e adicioná-lo ao nosso site de produção. Você pode até criar um script desse processo para copiá-lo para a pasta do seu site à medida que ele é construído.
Primeiro desenvolvimento reduzido do caso de teste
Algo que descobri como um dos principais benefícios de trabalhar dessa maneira é que isso realmente facilita a peça de suporte do navegador do quebra-cabeça. Não só é mais fácil ver qual CSS de fallback está sendo incluído com este componente, mas também se estivermos tendo problemas com um navegador, fica muito mais fácil depurá-los.
Quando você está lutando com um problema de navegador, o que geralmente lhe dizem para fazer é criar um caso de teste reduzido. Reduza o problema até a menor coisa que exiba o problema. Um componente em uma biblioteca de padrões geralmente já está muito próximo desse caso de teste reduzido. Certamente muito mais próximo do que se você estivesse tentando depurar um problema enquanto examina todo o seu site.
Além de facilitar a depuração do navegador, ter seus fallbacks incluídos junto com o restante do CSS facilita a remoção do código de fallback quando não for mais necessário, é óbvio que esse código de fallback é para esse componente. Eu sei que removê-lo não mudará a maneira como qualquer outra coisa é exibida.
Essa facilidade de organizar nosso código é realmente o motivo pelo qual o Fractal faz sentido mesmo em pequenos projetos. Dado que tendemos a usar Gulp e Sass de qualquer maneira (mesmo em projetos menores), adicionar Fractal à mistura não é uma grande sobrecarga. Não precisamos ver isso apenas para nossos projetos maiores, pois mesmo um site pequeno pode ter uma quantidade razoável de CSS.
Veja o código
Eu criei um projeto GitHub que tem todo o código mencionado no artigo. Eu sugeriria configurar o Fractal conforme descrito no artigo e, em seguida, pegar quaisquer bits - como o gulpfile ou o layout de visualização, do meu repositório.
Como referência adicional e para ver alguns projetos Fractal publicados, temos a versão publicada da Perch Pattern Library, e também a Pattern Library for 24 Ways (construída por Paul Robert Lloyd), que você pode dar uma olhada. Esses são bons exemplos de uma biblioteca de padrões que não é de site e uma mais tradicional usada para um site.
Como você gerencia CSS?
Gosto muito desta forma de trabalhar; ele me permite escrever CSS de maneira direta e progressivamente aprimorada. Dependendo do projeto, podemos incluir muito mais ferramentas e processamento de arquivos. Ou posso estar construindo um site simples, caso em que a configuração será praticamente como vimos neste artigo - com algum processamento leve do Sass. O fato de Fractal significa que podemos ter o mesmo processo para sites grandes e pequenos, para aplicativos da web ou sites. Isso significa que sempre podemos trabalhar de uma maneira familiar.
Isso funciona para nós, e espero que este artigo possa lhe dar algumas coisas para experimentar. No entanto, eu adoraria saber as maneiras pelas quais você e sua equipe abordaram o gerenciamento de CSS em seus projetos e os pontos fortes e fracos das abordagens que você tentou. Eu estaria especialmente interessado em ouvir alguém que desenvolveu um processo semelhante usando outra solução de biblioteca de padrões. Adicione suas experiências nos comentários.