17 min de leitura
No primeiro artigo do Guia Definitivo dos Design Tokens, vimos que um design token é uma decisão de design escrita como dado, em JSON, para ser independente de plataforma. Só que “JSON” não basta para duas ferramentas se entenderem. Cada uma poderia inventar sua própria estrutura, seus próprios nomes de propriedades e sua própria forma de escrever uma cor.
É para resolver isso que existe o DTCG, o Design Tokens Community Group. Neste artigo, você vai entender o que é essa especificação, como um arquivo de tokens é estruturado e, principalmente, quais tipos de token existem e como cada um é escrito, dos mais simples (cor, dimensão) aos compostos (sombra, tipografia, borda).
O que é o DTCG?
O DTCG (Design Tokens Community Group) é o grupo de comunidade do W3C que mantém a especificação do formato de arquivo para trocar design tokens entre ferramentas.
O grupo reúne pessoas de times de design system, fornecedores de ferramentas e especialistas independentes.
Em 28 de outubro de 2025, o grupo publicou a primeira versão estável, chamada 2025.10. Ela é dividida em módulos:
- Format Module: estrutura do arquivo, grupos, aliases, tipos simples e compostos.
- Color Module: como representar cores em diferentes espaços de cor.
- Resolver Module: como combinar conjuntos de tokens para gerar variações como temas e marcas (assunto do artigo sobre theming).
Um detalhe importante: a especificação é um relatório do Community Group. Ela não é um padrão oficial do W3C nem está na trilha de padrões do W3C. O próprio documento afirma que é considerada estável e destinada à implementação. Nota na margem: estável, mas não é padrão W3C
Na prática, o site oficial designtokens.org lista ferramentas como Figma, Style Dictionary, Tokens Studio, Penpot, Sketch e zeroheight entre os adotantes e participantes do grupo.
O objetivo é simples: um arquivo de tokens escrito hoje deve poder ser lido por ferramentas de design, de tradução (como o Style Dictionary) e de documentação, sem código de “cola” feito sob medida.
Como um arquivo de tokens é organizado
Um arquivo de tokens é um JSON em que os objetos com $value são tokens e os objetos sem $value são grupos, que servem para organizar e compor nomes.
Formato do arquivo
Arquivos de tokens são JSON. A especificação recomenda:
- Extensões:
.tokensou.tokens.json. - Tipo MIME:
application/design-tokens+json(oapplication/jsontambém é aceito).
Token, nome e valor
Um objeto que possui a propriedade $value é um token. A chave do objeto pai é o nome do token. Nome e valor são obrigatórios.
{
"color-brand": {
"$type": "color",
"$value": {
"colorSpace": "srgb",
"components": [0, 0.4, 0.8]
}
}
}
Todas as propriedades definidas pela especificação começam com $. Por isso, nomes de tokens e de grupos não podem começar com $. Além disso, os caracteres {, } e . são proibidos em qualquer nome, porque fazem parte da sintaxe de alias e de caminho.
Os nomes diferenciam maiúsculas de minúsculas. Mesmo assim, evite tokens que só se diferenciam pela caixa (font-size e FONT-SIZE): ao exportar para outras linguagens, eles podem gerar variáveis duplicadas.
Propriedades opcionais
Além de $value, um token pode ter:
| Propriedade | Para que serve |
|---|---|
$type | Declara o tipo do valor (color, dimension, shadow…) |
$description | Texto simples explicando o propósito do token |
$extensions | Dados proprietários de ferramentas ou times, com chave em notação de domínio reverso |
$deprecated | true, false ou uma string explicando a depreciação e o substituto |
Ferramentas devem preservar as $extensions que não entendem. Assim, um arquivo pode passar por várias ferramentas sem perder metadados.
Grupos
Um grupo é um objeto sem $value. Ele serve para organizar tokens e compor nomes. A especificação é explícita: grupos são arbitrários, e ferramentas não devem inferir significado ou tipo a partir do nome do grupo.
{
"color": {
"$type": "color",
"$description": "Cores do sistema",
"brand": {
"$value": {
"colorSpace": "srgb",
"components": [0, 0.4, 0.8]
}
}
}
}
Grupos também aceitam $type, $description, $deprecated, $extensions e $extends.
Herança de tipo
Todo token precisa de um tipo. Se o token não declara $type, o tipo é resolvido nesta ordem:
- Se o valor é um alias, o tipo é o do token referenciado.
- Senão, vale o
$typedo grupo pai mais próximo que declare um. - Senão, o token é inválido.
A especificação proíbe que ferramentas “adivinhem” o tipo olhando o valor. Um #ffffff sozinho não é automaticamente uma cor.
$root e $extends
Dois recursos de grupo merecem atenção.
O $root permite que um grupo tenha um token base ao lado de variações. Útil para casos como uma cor de destaque com versões clara e escura:
{
"color": {
"accent": {
"$root": {
"$type": "color",
"$value": { "colorSpace": "srgb", "components": [0.867, 0, 0] }
},
"light": {
"$type": "color",
"$value": { "colorSpace": "srgb", "components": [1, 0.133, 0.133] }
}
}
}
}
Aqui, {color.accent.$root} aponta para o token base. Já {color.accent} é inválido, pois referencia um grupo.
O $extends faz um grupo herdar tokens de outro, sobrescrevendo apenas o que mudar. A herança é uma mesclagem profunda: o token local substitui o herdado de mesmo caminho por completo.
{
"button": {
"$type": "color",
"background": { "$value": { "colorSpace": "srgb", "components": [0, 0.4, 0.8] } },
"text": { "$value": { "colorSpace": "srgb", "components": [1, 1, 1] } }
},
"button-danger": {
"$extends": "{button}",
"background": { "$value": { "colorSpace": "srgb", "components": [0.8, 0, 0.2] } }
}
}
Aliases: tokens que apontam para tokens
Como vimos no primeiro artigo, um alias é um token sem valor próprio, que aponta para outro. Na especificação, existem duas sintaxes.
Chaves (curly braces), a sintaxe principal. Sempre aponta para um token inteiro:
{
"color": {
"blue": {
"$type": "color",
"$value": { "colorSpace": "srgb", "components": [0, 0.4, 0.8] }
},
"action": { "$value": "{color.blue}" }
}
}
Como color.action não declara tipo, ele herda color do token referenciado.
JSON Pointer com $ref, para casos avançados. Permite apontar para uma propriedade dentro de um valor, como o primeiro componente de uma cor ou só o value de uma dimensão:
{
"space": {
"base": {
"$type": "dimension",
"$value": { "value": 16, "unit": "px" }
},
"base-rem": {
"$type": "dimension",
"$value": {
"value": { "$ref": "#/space/base/$value/value" },
"unit": "rem"
}
}
}
}
Regras que valem para os dois:
- Aliases encadeados são permitidos (A aponta para B, que aponta para C). A ferramenta segue a cadeia até achar um valor explícito.
- Referências circulares são erro e invalidam todos os tokens da cadeia.
- Ferramentas devem preservar as referências e só resolvê-las quando precisarem do valor final. É isso que permite mudar um primitivo e ver o efeito em todos os aliases.
Tipos simples
Todo token tem um tipo, e o valor precisa seguir a sintaxe daquele tipo. Se não seguir, o token é inválido. Pense como em uma linguagem tipada: o $type declara o que é permitido no $value.
color
O valor é um objeto com:
| Propriedade | Obrigatória | Descrição |
|---|---|---|
colorSpace | Sim | Espaço de cor |
components | Sim | Array com os componentes (números ou a palavra none) |
alpha | Não | Opacidade de 0 a 1. Se omitida, vale 1 |
hex | Não | Cor de reserva (fallback) em 6 dígitos |
{
"color-translucent": {
"$type": "color",
"$value": {
"colorSpace": "srgb",
"components": [0, 0, 0],
"alpha": 0.5,
"hex": "#000000"
}
}
}
Isso muda a forma como pensamos em cor: o #3B82F6 em string não é mais o formato do valor. Os componentes ficam numéricos e o hex vira apenas um fallback. O benefício é que a cor pode estar em qualquer espaço de cor suportado:
| Espaço | colorSpace | Componentes |
|---|---|---|
| sRGB | srgb | Vermelho, verde, azul (0 a 1) |
| sRGB linear | srgb-linear | Vermelho, verde, azul (0 a 1) |
| HSL | hsl | Matiz (0 a 360, exclusivo), saturação (0 a 100), luminosidade (0 a 100) |
| HWB | hwb | Matiz, brancura, negrura |
| CIELAB | lab | Luminosidade (0 a 100), a, b |
| LCH | lch | Luminosidade (0 a 100), croma, matiz |
| OKLAB | oklab | Luminosidade (0 a 1), a, b |
| OKLCH | oklch | Luminosidade (0 a 1), croma, matiz |
| Display P3 | display-p3 | Vermelho, verde, azul (0 a 1) |
| A98 RGB | a98-rgb | Vermelho, verde, azul (0 a 1) |
| ProPhoto RGB | prophoto-rgb | Vermelho, verde, azul (0 a 1) |
| Rec 2020 | rec2020 | Vermelho, verde, azul (0 a 1) |
| XYZ D65 | xyz-d65 | X, Y, Z |
| XYZ D50 | xyz-d50 | X, Y, Z |
A palavra-chave none (herdada do CSS Color Level 4) indica que um componente “não existe” ou não se aplica. Em um cinza em HSL, por exemplo, o matiz pode ser none em vez de 0. Visualmente é igual, mas faz diferença em interpolações e misturas de cor.
Para quem trabalha com cores em OKLCH, a especificação permite escrever:
{
"color-brand-500": {
"$type": "color",
"$value": {
"colorSpace": "oklch",
"components": [0.62, 0.19, 259]
}
}
}
O hex é opcional. Se incluí-lo como fallback, gere-o a partir do valor real com uma ferramenta de conversão, e não à mão.
dimension
Representa uma distância em um único eixo: posição, largura, altura, raio, espessura. O valor é um objeto com value (número) e unit, que só pode ser px ou rem: Nota na margem: em, vh e % ficam de fora
{
"space-200": {
"$type": "dimension",
"$value": { "value": 0.5, "unit": "rem" }
},
"space-none": {
"$type": "dimension",
"$value": { "value": 0, "unit": "px" }
}
}
Duas observações:
- A
unité obrigatória mesmo quando o valor é0. pxé um pixel “idealizado” (equivale adpno Android eptno iOS).remé um múltiplo do tamanho de fonte padrão do sistema, que o usuário pode configurar. Em plataformas sem equivalente arem, a ferramenta de tradução pode converter para px assumindo 16px.
fontFamily
Um nome de fonte (string) ou um array de nomes, do mais ao menos preferido:
{
"font-family-body": {
"$type": "fontFamily",
"$value": ["Inter", "Helvetica", "Arial", "sans-serif"]
}
}
fontWeight
Um número entre 1 e 1000 (segundo o eixo wght do OpenType) ou um nome predefinido:
| Número | Nomes aceitos |
|---|---|
| 100 | thin, hairline |
| 200 | extra-light, ultra-light |
| 300 | light |
| 400 | normal, regular, book |
| 500 | medium |
| 600 | semi-bold, demi-bold |
| 700 | bold |
| 800 | extra-bold, ultra-bold |
| 900 | black, heavy |
| 950 | extra-black, ultra-black |
Valores fora de 1 a 1000, ou nomes diferentes (inclusive com outra caixa), são inválidos. Isso suporta fontes variáveis: 350 é um valor válido.
{
"font-weight-body": { "$type": "fontWeight", "$value": 400 },
"font-weight-strong": { "$type": "fontWeight", "$value": "bold" }
}
duration
Tempo de uma animação, como objeto com value e unit (ms ou s):
{
"duration-fast": {
"$type": "duration",
"$value": { "value": 150, "unit": "ms" }
}
}
cubicBezier
A curva de progressão de uma animação (aceleração, desaceleração, retorno). É um array com quatro números [P1x, P1y, P2x, P2y]. Os valores de x ficam restritos ao intervalo de 0 a 1; os de y podem ser qualquer número.
{
"easing-standard": {
"$type": "cubicBezier",
"$value": [0.4, 0, 0.2, 1]
}
}
number
Um número JSON puro. Serve para valores sem unidade, como posições de parada de gradiente ou altura de linha como multiplicador:
{
"line-height-body": { "$type": "number", "$value": 1.5 }
}
O que ainda não existe como tipo
A especificação reconhece lacunas. Estilo de fonte (normal, italic), porcentagem/razão (como opacidade) e arquivos (assets) aparecem apenas como tipos “a documentar”. Enquanto isso, o caminho mais comum é usar number para valores como opacidade e $extensions para necessidades específicas. Trate qualquer uso fora do que está normatizado como escolha do seu time, e não como parte do padrão.
Tokens compostos
Alguns aspectos de interface são combinações de vários valores que sempre andam juntos. Uma sombra é feita de cor, deslocamentos, desfoque e espalhamento. Uma tipografia é feita de família, tamanho, peso, espaçamento e altura de linha.
Um token composto tem como valor um objeto (ou array) com estrutura fixa. Cada subvalor tem um tipo conhecido e pode ser explícito ou um alias para um token do tipo correto.
Isso é diferente de um grupo:
| Grupo | Token composto | |
|---|---|---|
| É um token? | Não | Sim |
| Pode ser referenciado por alias? | Não | Sim |
| Estrutura | Livre | Fixa, definida pelo tipo |
| Pode ter subvalores extras? | Sim | Não: torna o token inválido |
strokeStyle
Estilo de traço de linhas e bordas. Aceita uma de duas formas, mutuamente exclusivas:
String, com os valores equivalentes ao CSS: solid, dashed, dotted, double, groove, ridge, outset, inset.
{
"stroke-focus": { "$type": "strokeStyle", "$value": "dashed" }
}
Objeto, com dashArray (lista de dimensões alternando traço e espaço) e lineCap (round, butt ou square):
{
"stroke-alert": {
"$type": "strokeStyle",
"$value": {
"dashArray": [
{ "value": 0.5, "unit": "rem" },
{ "value": 0.25, "unit": "rem" }
],
"lineCap": "round"
}
}
}
Como nem toda plataforma dá conta de todos os estilos (o CSS não permite controlar o padrão de tracejado, e o Figma não suporta inset, outset ou double), as ferramentas devem recorrer à aproximação mais próxima que suportam.
border
Objeto com color, width e style (um strokeStyle):
{
"border-subtle": {
"$type": "border",
"$value": {
"color": "{color.border.subtle}",
"width": { "value": 1, "unit": "px" },
"style": "solid"
}
}
}
transition
Objeto com duration, delay e timingFunction (uma cubicBezier):
{
"transition-emphasis": {
"$type": "transition",
"$value": {
"duration": { "value": 200, "unit": "ms" },
"delay": { "value": 0, "unit": "ms" },
"timingFunction": [0.5, 0, 1, 1]
}
}
}
A própria especificação levanta uma questão em aberto: esses parâmetros, sozinhos, não dizem qual propriedade é animada nem os estados de início e fim.
shadow
Objeto com color, offsetX, offsetY, blur, spread e, opcionalmente, inset (sombra interna, false por padrão). O valor pode ser um objeto ou um array de sombras, o que permite sombras em camadas:
{
"shadow-medium": {
"$type": "shadow",
"$value": {
"color": "{color.shadow-050}",
"offsetX": "{space.small}",
"offsetY": "{space.small}",
"blur": { "value": 1.5, "unit": "rem" },
"spread": { "value": 0, "unit": "rem" }
}
},
"shadow-elevated": {
"$type": "shadow",
"$value": [
"{shadow-medium}",
{
"color": { "colorSpace": "srgb", "components": [0, 0, 0], "alpha": 0.2 },
"offsetX": { "value": 0, "unit": "px" },
"offsetY": { "value": 24, "unit": "px" },
"blur": { "value": 44, "unit": "px" },
"spread": { "value": 0, "unit": "px" }
}
]
}
}
No array, cada elemento pode ser uma sombra explícita ou uma referência a outro token shadow. Referências resolvem para um único objeto e não se “achatam” dentro do array.
gradient
Um array de paradas (stops), cada uma com color e position. A posição é um número de 0 (início) a 1 (fim); fora desse intervalo, o valor é limitado (clamp) a ele. Se não houver parada em 0 ou 1, a cor da parada mais próxima se estende até a ponta.
{
"gradient-brand": {
"$type": "gradient",
"$value": [
{ "color": "{color.brand.500}", "position": 0 },
{ "color": "{color.brand.700}", "position": 1 }
]
}
}
A própria especificação pergunta se o tipo deveria indicar o formato do gradiente (linear, radial, cônico). Hoje, esse tipo descreve apenas as paradas.
typography
Objeto com fontFamily, fontSize, fontWeight, letterSpacing e lineHeight. Cada propriedade é um valor do tipo correspondente ou um alias. A lineHeight é um number, interpretado como multiplicador do fontSize.
{
"text-heading-1": {
"$type": "typography",
"$value": {
"fontFamily": "{font.family.heading}",
"fontSize": { "value": 2.5, "unit": "rem" },
"fontWeight": 700,
"letterSpacing": { "value": 0, "unit": "px" },
"lineHeight": 1.2
}
}
}
Também é possível reutilizar partes de uma tipografia em outra, usando $ref para apontar só para a fontFamily ou a lineHeight de um token-base.
Resumo dos tipos
| Tipo | Valor | Tipo de token |
|---|---|---|
color | Objeto com colorSpace, components, alpha, hex | Simples |
dimension | { value, unit } com px ou rem | Simples |
fontFamily | String ou array de strings | Simples |
fontWeight | Número de 1 a 1000 ou nome predefinido | Simples |
duration | { value, unit } com ms ou s | Simples |
cubicBezier | Array de 4 números | Simples |
number | Número JSON | Simples |
strokeStyle | String predefinida ou { dashArray, lineCap } | Composto |
border | { color, width, style } | Composto |
transition | { duration, delay, timingFunction } | Composto |
shadow | Objeto ou array de { color, offsetX, offsetY, blur, spread, inset } | Composto |
gradient | Array de { color, position } | Composto |
typography | { fontFamily, fontSize, fontWeight, letterSpacing, lineHeight } | Composto |
O que isso muda na prática
1. Atenção à versão do formato. Rascunhos anteriores da especificação (e muitos exemplos na internet) escreviam cores como "#3B82F6" e dimensões como "16px". A versão 2025.10 usa objetos. Se você tem arquivos antigos, é preciso migrar. Antes de adotar o formato novo, confirme na documentação da sua ferramenta de tradução e do seu plugin de design qual versão do formato ela lê. A ferramenta pode exigir uma versão específica ou uma configuração para isso.
2. Os tipos importam para a tradução. É o $type que permite a uma ferramenta transformar uma dimension em rem para web e em pt para iOS, ou uma color em OKLCH em um fallback hexadecimal.
3. Nem tudo cabe em um tipo composto. Se o seu sistema precisa de um estilo de fonte, de uma opacidade com nome próprio ou de um asset, você vai usar number, aliases e $extensions. Documente essas decisões, pois elas não são interoperáveis por padrão.
4. Composto ou separado? Você pode representar uma tipografia como um token typography ou como cinco tokens simples (font-size, font-weight etc.). O composto garante que as propriedades andem juntas e simplifica o uso em ferramentas de design. Os tokens separados são mais flexíveis para consumir em código. Muitos times mantêm os dois. É uma decisão que depende das plataformas e das ferramentas que você usa.
Este é o segundo artigo do Guia Definitivo de Design Tokens. No próximo, vamos falar sobre nomenclatura de tokens: como nomear, estruturar e organizar tokens para que o sistema continue legível quando crescer.
Para não perder as próximas publicações, inscreva-se na newsletter e me siga no LinkedIn.
Fontes
- Design Tokens Format Module 2025.10
- Design Tokens Color Module 2025.10
- Design Tokens Resolver Module 2025.10
- designtokens.org: lista de adotantes e participantes.
- Anúncio da primeira versão estável