Como fizemos amigos do SCSS com variáveis ​​CSS usando o tema do kit de interface do usuário


Olá pessoal, meu nome é Vitalik, sou desenvolvedor sênior de front-end na Skyeng. Nossa equipe está criando uma plataforma Vimbox on-line para aprender inglês. Há cerca de um ano, o designer e eu concluímos um pequeno kit de interface do usuário que desenraizou o caos na interface e na base de código.


Aconteceu que não éramos os únicos na empresa que queriam o kit de interface do usuário, e outras equipes começaram a nos pedir conselhos sobre "como escrever o seu próprio". Conseguimos dissuadi-los desse empreendimento, prometendo moderar o nosso - isso salvou a empresa centenas de horas de desenvolvimento. Escolhendo uma solução, examinamos o Material Angular, montagens personalizadas e Variáveis ​​CSS e, por fim, resolvemos o último, apesar de sua fraca compatibilidade com o SCSS, a base do kit de interface do usuário existente. Sob o corte - detalhes do que fizemos.



O problema


O primeiro kit de interface do usuário consistia em fontes, uma paleta, um conjunto de elementos para a criação de formulários (entrada, botão, etc.), um sistema para gerenciar ícones svg. Pop-up e dica de ferramenta baseada em materiais angulares também foram implementados. Ele foi preso por trabalhar apenas com o Vimbox "clássico": muitas coisas foram deliberadamente costuradas com firmeza e não permitiram mudanças externas. E a Skyeng começou a ter novos produtos na mesma plataforma, por exemplo, para crianças.


Os desenvolvedores de novas direções, sabendo que temos algo, vieram pedir conselhos. E, para nossa surpresa, eles já vinham com os layouts de seus kits de interface do usuário: eles iriam desenvolver suas soluções do zero, porque eles precisavam de uma aparência diferente para os componentes. Ficou claro que algo estava errado, e propusemos refinar nossa biblioteca, acrescentando a possibilidade de seu tema.


O argumento era simples: projetar nosso kit de interface do usuário levou 200 horas de design de UX e mais de 500 horas de desenvolvimento. Este é o tempo necessário para criar um sistema de fontes, cores e cerca de 10 componentes básicos. Assim, se você escrever uma biblioteca separada para cada produto, a empresa gastará N * 500 horas de tempo de desenvolvimento. Pensamos que melhorar o nosso kit de interface do usuário seria mais barato, além disso, essa etapa não precisaria ser repetida para cada produto.


Nossos argumentos foram aceitos, as áreas relacionadas concordaram em esperar e partimos para procurar uma solução técnica.


Dados de origem


Nossas ferramentas: Angular, SCSS.


Suportamos apenas navegadores modernos e, com algumas limitações, o IE11. O que facilita a vida.


Todos os nossos componentes do kit de interface do usuário foram imbuídos de estilos comuns que empilhamos no UI kit.var.scss como constantes do SCSS:


 @mixin fontSizeXl { @include fontSize(18px, 26px); } $colorSkillListening: #9679e0; $colorSkillListeningText: #7754d1; $colorSkillListeningBackground: mix($colorSkillListening, #ffffff, 16%); $colorSkillListeningBackgroundHover: mix($colorSkillListening, #ffffff, 8%); 

Desafio


  • Todos os novos produtos são montados a partir de elementos existentes no Vimbox "adulto" - salas de aula, contas pessoais etc.
  • Os designers devem ter ampla liberdade para implementar idéias criativas, recursos distintos e requisitos específicos de novos produtos.
  • Ao mesmo tempo, a continuidade permanece, ou seja, não importa quão cores ácidas e fontes loucas o designer invente, o pertencimento do resultado de seu trabalho ao ecossistema Skyeng permanece óbvio.
  • Tudo isso é adicionado ao kit de interface do usuário existente, mantendo todas as suas vantagens.

Vamos lá!


Assim, o resultado é esperado de nós ontem, devemos realizar rapidamente análises técnicas e discutir opções. Nas primeiras reuniões, identificamos um círculo de possíveis soluções:


Material angular


Como não gostamos de escrever bicicletas, voltamos primeiro ao Material Angular. Nos componentes, os estilos dinâmicos são movidos para um arquivo {component}-theme.scss . Esses estilos são vinculados ao seletor global do componente.


vantagenscontras
não há necessidade de adicionar novas tecnologias ao projetoprecisa fazer alterações em cada componente
a maior quantidade de trabalho
tem que sacrificar o encapsulamento de estilo

Variáveis ​​CSS


Temos um ótimo motivo para experimentar as variáveis ​​CSS da moda. O plano é transplantar as partes personalizadas do kit de interface do usuário para variáveis ​​CSS. Os componentes usam as mesmas constantes SCSS, mas em vez de valores específicos, vars CSS são gravados nelas.


vantagenscontras
possui escopotem que adicionar polyfill para o IE
tecnologia de navegador nativa (exceto IE)não poderemos usar funções SCSS
abre a possibilidade de usar CSS vars em outras tarefas

Construções personalizadas


Adoramos soluções simples, por que não tentar mudar a montagem? Cada equipe cria seu próprio arquivo com configurações de tema. Ao montar para todos os temas customizados, pacotes configuráveis ​​separados com seu próprio tema são criados.


vantagenscontras
não há necessidade de alterar o código da bibliotecaconfiguração de montagem de biblioteca mais complexa
complicação de implantação de biblioteca
fazendo alterações, é necessário remontar artefatos para todos os projetos
não há possibilidade de alternar temas "dinâmicos". Será difícil adicionar o modo noturno

Solução


Durante uma semana, estudamos cada opção, discutimos, adiamos a decisão e estudamos novamente.


Adoramos novas tecnologias e as monitoramos, mas apenas as implementamos se elas nos derem bônus reais. Sabíamos das variáveis ​​CSS, queríamos experimentá-las, mas a falta de funções do SCSS causou muita tristeza. No entanto, as vantagens dessa opção eram óbvias, decidimos descobrir como e quais funções SCSS usamos, se é possível fazer amizade com vars CSS.


Entendendo CSS vs SCSS


Depois de experimentar, percebemos que o principal problema é a falta de suporte para #hex no CSS: no SCSS, escrevemos rgba(#ffffff, 0.4) , e no CSS a mesma coisa requer um conjunto diferente de parâmetros - rgba(255, 255, 255, 0.4) . Tudo funciona para nós com #hex, e nós realmente, realmente não queremos mudar isso. Encontramos soluções, direi por ordem de admissão.


Clarear e escurecer


Nosso designer criou uma paleta composta por um pequeno número de cores básicas, expandindo-se devido às funções SCSS de lighten e darken :


 // $color –   base: $color, background: mix($color, #ffffff, 16%), backgroundHover: mix($color, #ffffff, 8%), hover: lighten($color, 5), focused: darken($color, 5), ...more transformations... 

Tentamos encontrar um análogo de lighten e darken no CSS, mas não encontramos nada. Pensamos por vários dias até percebermos que, para personalização, precisamos nos livrar dessas funções dentro da biblioteca, removendo-as. Afinal, cada equipe pode querer criar sua própria fórmula para mudar de cor ao mudar o foco - por exemplo, colegas da Kids precisam de mais contraste.


A solução simples foi transferir nossas transformações para o lado da plataforma que inicializará o tópico. E para a plataforma, estamos escrevendo uma função que cria automaticamente os valores necessários:


 @function getMainColors($color, $colorText) { $colors: ( text: $colorText, base: $color, background: mix($color, #ffffff, 16%), backgroundHover: mix($color, #ffffff, 8%), lightenLess: lighten($color, 5), darkenLess: darken($color, 5), lightenMore: lighten($color, 20), ); @return $colors; } 

A plataforma usa ao inicializar cores:


 //platform $colorValues: ( brand: getMainColors(#5d9cec, #4287df), positive: getMainColors(#8cc152, #55a900), accent: getMainColors(#ff3d6f, #ff255d), wrong: getMainColors(#ff6666, #fe4f44), ) 

RGBA


Em nosso kit de interface do usuário, usamos a função rgba . Com isso, ajustamos a transparência das cores de base. Mas se o rgba funcionar com #hex no SCSS, o CSS não poderá. Eu tive que escrever uma função que decompõe o valor #hex em r / g / b:


 // returns `r, g, b` from `#hex` for `rgba(var(--smth))` usage @function rgbValuesFromHex($hex) { @return red($hex), green($hex), blue($hex); } 

Bem, como não queremos gerar valores RGB com alças para toda a paleta, criamos uma função separada que faz isso recursivamente para cada cor da coleção:


 // adds `fieldRgb: r, g, b` fields to map for each `field: #hex` for `rgba(var(--smth-rgb))` usage @function withRgbValues($map) { $rgbValues: (); @each $name, $value in $map { $formattedValue: (); @if type-of($value) == 'map' { $rgbValues: map-merge($rgbValues, (#{$name}: withRgbValues($value))); } @else { //   , rgb    Rgb   $rgbValues: map-merge($rgbValues, (#{$name}Rgb: rgbValuesFromHex($value))); } } @return map-merge($map, $rgbValues); } 

Como resultado, a inicialização da paleta com os valores RGB gerados fica assim:


 $colorValues: withRgbValues( ( text: ( base: #242d34, secondary: #50575c, label: #73797d, placeholder: #969b9e, inversed: #ffffff, inversedSecondary: #dadada, ), brand: getMainColors(#5d9cec, #4287df), positive: getMainColors(#8cc152, #55a900), accent: getMainColors(#ff3d6f, #ff255d), wrong: getMainColors(#ff6666, #fe4f44), //...etc 

A saída é um mapa de cores SCSS, que pode ser transformado em um método que o transforma em variáveis ​​CSS. Para obter o valor do tema RGB, escrevemos uma função:


 @function getUiKitRgbVar($path...) { $path: set-nth($path, -1, #{nth($path, -1)}Rgb); //       @return getFromMap($uiKitBaseVars, $path...); } //  border-color: rgba(getUiKitRgbVar(color, brand, base), $opacity64); 

Transforme const SCSS em CSS vars


A primeira etapa é criar uma estrutura de espelho (semelhante ao SCSS) que armazene nomes de variáveis ​​CSS:


 $colorCssVars: withRgbCssVars( ( text: ( base: getColorCssVar(text, base), secondary: getColorCssVar(text, secondary), label: getColorCssVar(text, label), placeholder: getColorCssVar(text, placeholder), inversed: getColorCssVar(text, inversed), //   

getColorCssVar - um método que adiciona prefixos aos nomes de variáveis. Adicione o prefixo --sky para evitar colisões com bibliotecas externas. E também adicione o prefixo da biblioteca de - UI kit a --sky para evitar conflitos com bibliotecas internas. Descobriu --sky- UI kit :


 @function getColorCssVar($parts...) { @return getUiKitCssVar(color, $parts...); } @function getUiKitCssVar($parts...) { $uiKitCssVarPrefix: '--sky- UI kit'; $cssVar: $uiKitCssVarPrefix; @each $part in $parts { $cssVar: $cssVar + '-' + $part; } @return $cssVar; } 

Por exemplo, para getColorCssVar(text, base) obtemos --sky- UI kit-color-text-base na saída.


O toque final é uma combinação recursiva que inicializa valores da estrutura SCSS para variáveis ​​com nomes da estrutura Var CSS:


 //       :root { @include uiKitThemeCssVars($uiKitDefaultTheme); //uiKitDefaultTheme – SCSS       } @mixin uiKitThemeCssVars($theme) { $cssVarsList: createVarsList($theme, $uiKitBaseCssVars); //18+,    Map  List, $uiKitBaseCssVars   css  @each $cssVar, $value in $cssVarsList { #{$cssVar}: $value; } } 

Um exemplo de uso de um tema na plataforma:


 .popup { font-family: getUiKitVar(font, family); background-color: getUiKitVar(color, background, base); ... } 

Qual é o resultado


Conseguimos usar variáveis ​​CSS, mantendo a capacidade de usar funções SCSS. Criou a capacidade de personalizar a aparência dos componentes. Escrevemos alguns métodos recursivos para automatizar a extensão do tópico. Bem e mais importante - passou 30 horas de desenvolvimento em vez de N * 500.


Lucro!

Source: https://habr.com/ru/post/pt459194/


All Articles