Design System: Style Dictionary em escala

Cesar Morigaki
iFood Tech
Published in
7 min readApr 17, 2023
Foto de Steve Johnson na Unsplash

O style dictionary é um framework de geração de código que faz a ponte entre design e tecnologia na pauta de design tokens. Apesar de ser bem configurável e expansível, lidar com um design system complexo exige também uma boa arquitetura e design de código para superar várias dificuldades advindas deste cenário.

Com base na experiência que tivemos na construção do Design System do iFood, abordaremos vários pontos importantes da integração do Style Dictionary (v3.7.1) como parte da solução escalável que implementamos.

Design System do iFood

A camada de tokens do nosso design system foi pensada e construída para atender diversos produtos, marcas, temas e plataformas do ecossistema iFood. Esse potencial de grandes quantidades de combinações de input tem reflexo direto nas decisões que tomamos em relação à especificação, estruturação, e código junto ao style dictionary.

Implementando em escala

Com o style dictionary é possível ter resultados “out of the box” tendo um design system tem que siga a estrutura pré-definida de input e que o output atenda ao projeto. Nesse caso é preciso adequar-se à ele para ter resultados rápidos. Por isso, no nosso cenário complexo e de inúmeros requisitos, tivemos várias dificuldades. Dentre elas:

  • Os transformers pré-definidos seguem uma estrutura semântica de tokens simples que não atendeu a complexidade do nosso design system
  • Os transformers e matchers pré-definidos não seguem um padrão de nome e referência
  • Sem suporte específico à tematização
  • Suporte limitado à geração de código por referência

Vamos entrar nos detalhes da nossa solução abordando essa sequência de tópicos:

  1. Especificação dos tokens do Design System
  2. Limpeza do input
  3. Componentes do Style Dictionary
  4. Suporte à tematização
  5. Gerar código por referência

1. Especificação de tokens

Várias complexidades e dificuldades na construção da geração de código podem surgir caso a especificação dos tokens não esteja bem padronizada e estruturada previamente. Pontos de atenção:

Estrutura semântica:

Além da importância semântica atrelada ao token, definir a estrutura de composição do nome facilitará o processo de criação de novos token e ainda estabelecerá um padrão claro para manipulação desses tokens na fase de geração de código.

Essa é a estrutura que estamos usando para tokens não contextuais:

Uma única dimensão em um mesma folha de tokens:

Folha de tokens: são tokens agrupados em uma mesma categoria. Ex: border, size, color.

Dimensão: é a unidade de medida de algo. Ex: comprimento, massa, percentual, cor, tempo.

Uma folha de tokens deve conter tokens com valores de uma única dimensão. Na ocorrência de mais de uma dimensão, precisamos repensar e quebrá-los em um outra categoria. A mistura de dimensões dificultará na seleção para transformação dos tokens na geração de código, resultando em regras de exceção difíceis de serem mantidas.

Uma única escala por dimensão

Escala: unidade de medida usada em uma dimensão. No sistema métrico, por exemplo, temos as escalas quilômetro (km), metro (m), centímetro (cm).

Precisamos estabelecer o padrão que receberemos os valores dos nossos tokens. Dessa forma criamos uma independência do valor vir com o símbolo e facilitamos a transformação de valores dos tokens.

2. Limpeza do input

Remover indicação de unidade e escala

Utilizar valores numéricos puros serão mais fáceis de serem trabalhados uma vez que já foram estabelecidas as escalas esperadas do input.

Valores agnósticos à tecnologia

Valores complexos e/ou compostos podem cair nesse ponto. Devemos ficar atentos à eles especialmente quando ainda geramos código para uma única tecnologia e/ou utilizamos o json de tokens como fonte de verdade para alguma ferramenta específica de design.

Além disso, decompondo valores complexos, facilitamos a referência de tokens mais básicos em sua composição.

3. Componentes do Style Dictionary

Do input ao output do Style Dictionary, passamos pelos seguintes componentes:

  • Matchers
  • Transformers
  • Formats
  • Actions

O Style Dictionary possui implementações de transformers e formatters disponíveis prontas para uso. Essas implementações facilitam uma geração rápida de código porém se apoiam em uma estrutura semântica de tokens simples e rígida. Como estamos indo além com uma estrutura complexa adequada à nossa necessidade, optamos em seguir com novas implementações. Precisamos assegurar padronizações e responsabilidades a serem seguidas para ter um código escalável.

Padronização de matchers

Após feito um bom trabalho na especificação e limpeza dos valores do json, precisamos de matchers de dois grupos distintos:

  • Matchers de folha de tokens
  • Matchers de dimensão

Matchers de folha de tokens
Os matchers de folha de tokens são utilizados no filtro da configuração do que precisa ser gerado na plataforma, quando a intenção é gerar um arquivo por folha de token.

Padrão de estrutura de arquivo e nomes:

  • file path: matchers/token/is{token_name}Token.ts

Matchers de dimensão
Os matchers de dimensão são utilizados como filtro de um transformer.

Padrão de estrutura de arquivo e nomes:

  • file path: matchers/dimension/is{dimension_name}Dimension.ts

Não é ideal mas podemos ter a necessidade de algum matcher específico de token para manter alguma simplicidade na especificação. O tradeoff deve ser uma exceção.

Padronização de transformers

Para os transformers, precisamos de 2 grupos distintos:

  • Transformers de escala
  • Transformers de plataforma

Transformers de escala
Esses transformers devem ser agnósticos à plataforma e apenas mudar a escala da dimensão. Usam matchers de dimensão.

Padrão de estrutura de arquivo e nomes:

  • file path: transforms/value/scale/{from_dimension}To{to_scale}.ts
  • name: {from_dimension}/{to_scale}

Transformers de plataforma
Essa transformação é específica de plataforma/tecnologia e seu uso estará atrelado com a configuração de alguma plataforma. Usam matchers de dimensão.

Padrão de estrutura de arquivo e nomes:

  • file path: transforms/value/platform/{platform}/{from_dimension}To{to_type}.ts
  • name: {from_dimension}/{platform}/{to_type}

Não faça a transformação de escala junto de um platform transformer. Prefira quebrar em 2 transformers diferentes e fazer a composição deles na configuração.

Formats

Responsáveis pelo template de output, são específicos de plataforma.

Padrão de estrutura de arquivo e nomes:

  • file path: formats/{platform}/{template}.ts
  • name: {platform}/{template}

Actions

As actions são executadas após a geração de arquivos configurados. Com elas podemos executar operações adicionais nos arquivos gerados e/ou adicionar mais código. Exemplos:

  • Quebra de arquivos. Cada token de gradiente em Android para xml deve ficar em um arquivo separado (drawable). Geramos a folha inteira de gradiente em um arquivo e quebramos em vários através de uma action.
  • Helpers adicionais. Inclusão de arquivos adicionais que ajudam a fazer uso de tokens como por exemplo uma extension de algum componente.
  • Geração de interfaces. Um caso de uso é gerar as interfaces de acordo com o código tematizável gerado para fazer uso no aplicativo através de uma abstração.

Padrão de estrutura de arquivo e nomes:

  • file path: actions/{platform}/{name}.ts ou actions/{generic}/{name}.ts
  • name: {platform}/{name}

4. Suporte à tematização

Não existe um suporte específico em relação à temas mas podemos aplicar a mesma estratégia de multi-branding que existe em um exemplo do Style Dictionary.

Config

Para gerar cada config, para cada atributo que contribui com a uma variante adicionamos um nível de hierarquia de diretórios de tokens. No nosso caso também criamos um brand “global” para expressar os tokens base de qualquer brand.

A input de tokens de cada configuração são resultado do merge do global com o brand.

  • Hierarquia: tokens/{brand}/{theme}/
  • Exemplo:

Outro ponto importante é gerar abstrações para as folhas de tokens tematizáveis através de actions.

5. Gerar código por referência

Essa funcionalidade a princípio não é essencial mas traz o benefício de ter um código mais legível e portanto facilmente analisável.

O style dictionary possui um suporte bem limitado para gerar código por referência através do file.options.outputReferences e dictionary.usesReference. Essa funcionalidade é bem mais complexa e merece um artigo a parte só para mostrar como estendemos o código para gerar código com referência da forma que queríamos.

Conclusão

O style dictionary é um framework poderoso e que exige um trabalho cuidadoso na extensão para adequar à necessidades e trazer robustez em um design system complexo. Passamos pelo essencial da etapa de geração de código de tokens e deixo também um repositório de exemplo com parte abordada nesse artigo.

--

--