Temas parecem um projeto pequeno: escolher algumas cores e trocar o arquivo de configuração do editor. Foi exatamente com essa expectativa que comecei o Underground, um tema escuro para VS Code e Kiro IDE. No caminho, descobri que um tema bom não é uma coleção de cores bonitas; é um pequeno sistema de design aplicado a muitos contextos.
O Underground nasceu como projeto de estudo. A proposta era simples: uma interface escura, discreta, com contraste confortável e um verde quente como ponto de atenção. Depois, o projeto cresceu para incluir o terminal integrado do editor, o Windows Terminal e o Terminal.app do macOS — todos orientados pela mesma paleta.
Neste artigo, compartilho o processo, as decisões e os problemas que encontrei. O repositório está disponível em GitHub.
Comecei pela intenção, não pelas cores
Antes de escrever JSON, defini a sensação que queria criar:
- fundo escuro sem ser preto absoluto;
- pouca competição visual entre as áreas da interface;
- cores de sintaxe que ajudem a escanear o código;
- cores semânticas previsíveis para erro, aviso, Git e conflitos;
- terminal e editor com a mesma identidade.
Essa primeira etapa evitou uma armadilha comum: atribuir cores isoladamente à medida que os elementos aparecem. Em vez disso, cada escolha deveria responder a uma função. O verde, por exemplo, não existe apenas porque eu gosto dele; ele marca elementos ativos e também palavras-chave, parâmetros e tags. Ele é uma cor de atenção moderada, não uma cor usada em todo lugar.
Uma paleta pequena, com papéis claros
O núcleo do tema é composto por poucos tons:
| Papel | Cor | Uso principal |
|---|---|---|
| Fundo | #222222 |
editor, barra lateral e painéis |
| Superfície elevada | #272727 |
abas ativas, inputs e status bar |
| Texto principal | #ffffffde |
conteúdo de maior prioridade |
| Texto secundário | #ffffff99 |
descrições e informações auxiliares |
| Acento verde | #9bd4b2 |
atividade, parâmetros e Git modificado |
| Azul | #90aed3 |
strings, funções e números |
| Roxo | #ceb0d3 |
tipos, interfaces e atributos |
| Comentários | #8a8a8a |
conteúdo propositalmente secundário |
Um detalhe importante é o de em #ffffffde. Em temas do VS Code, também podemos usar hex com canal alfa. Assim, em vez de inventar muitos cinzas diferentes, usei branco com transparências distintas para criar uma escala de hierarquia: texto principal, secundário e sutil.
Essa abordagem deixou as decisões mais fáceis de manter. Quando percebi que comentários estavam apagados demais, por exemplo, não precisei redesenhar o tema inteiro: subi seu tom de #6a6a6a para #8a8a8a, preservando seu papel visual e melhorando a legibilidade.
A estrutura mínima de uma extensão de tema
Uma extensão de tema do VS Code pode ser surpreendentemente pequena. No caso do Underground, os dois arquivos essenciais são:
editors/vscode/
├── package.json
└── themes/
└── underground-color-theme.json
O package.json registra o tema na seção contributes:
{
"contributes": {
"themes": [
{
"label": "Underground",
"uiTheme": "vs-dark",
"path": "./themes/underground-color-theme.json"
}
]
}
}
O arquivo de tema contém duas partes que vale separar mentalmente:
-
colors: a interface do VS Code — editor, abas, listas, widgets, terminal e estado do Git. -
tokenColorsesemanticTokenColors: a aparência do código.
Essa separação parece óbvia, mas foi uma boa forma de organizar o trabalho. Primeiro deixei o ambiente confortável para navegar; depois cuidei da leitura do código.
Tokens de sintaxe: cor é uma ferramenta de leitura
Para a primeira camada de sintaxe, associei grupos de scopes a papéis visuais. Uma versão reduzida da configuração é esta:
{
"scope": ["keyword", "storage", "variable.parameter", "entity.name.tag"],
"settings": { "foreground": "#9bd4b2" }
},
{
"scope": ["string", "support.function", "constant.numeric"],
"settings": { "foreground": "#90aed3" }
},
{
"scope": ["entity.name.type", "entity.other.attribute-name"],
"settings": { "foreground": "#ceb0d3" }
}
O ponto não é decorar scopes, e sim testar em linguagens diferentes. A mesma categoria pode receber scopes diferentes em TypeScript, HTML, JSON ou Markdown. Por isso mantive uma amostra de código para abrir no editor enquanto ajustava o tema.
Também adicionei semanticTokenColors. Essa camada moderna permite ao editor informar significados como type, function, parameter e variable.readonly, especialmente útil em TypeScript e JavaScript. O resultado é mais consistente do que depender exclusivamente da gramática textual.
A parte que quase sempre fica esquecida: a interface inteira
Colorir strings e palavras-chave é só o começo. Um tema realmente utilizável precisa lidar com estados menos frequentes: busca, sugestões, mensagens de validação, diff, merge conflict, Peek Definition, foco por teclado e decoração de Git.
Esses casos revelam problemas que uma captura de tela comum não mostra. No Underground, encontrei dois exemplos marcantes:
- os números de linha tinham contraste insuficiente e praticamente desapareciam;
- o widget de Peek Definition não tinha cores próprias e usava um fundo claro inesperado.
Corrigi o primeiro caso elevando o tom dos números inativos e mantendo o número ativo em branco. Para o segundo, foi preciso definir as chaves peekView*, usando o fundo elevado e a seleção já existentes na paleta.
Essa experiência mudou meu critério de pronto: um tema não está pronto quando o arquivo de exemplo está bonito. Ele está pronto depois de passar por fluxos reais de edição, busca, autocomplete, Git e resolução de conflitos.
Diferenças e conflitos também comunicam significado
Para diffs, evitei blocos muito saturados. Linhas adicionadas usam verde com baixa opacidade; removidas usam vermelho com baixa opacidade. As palavras alteradas recebem uma opacidade um pouco maior que a linha inteira, para criar hierarquia.
Nos conflitos de merge, mantive uma convenção semântica:
- versão atual: verde;
- versão recebida: azul;
- base comum: cinza;
- conflito ainda não resolvido: borda amarela.
É uma decisão pequena, mas útil: as cores deixam de ser decoração e passam a explicar o estado da informação.
Um tema, mais de um ambiente
Eu queria que abrir o terminal não parecesse mudar de aplicação. Por isso, documentei a paleta em PALETTE.md e usei o tema do VS Code como fonte canônica. A partir dos valores terminal.ansi*, criei:
- um esquema JSON para o Windows Terminal;
- um perfil
.terminalpara o Terminal.app do macOS.
Há uma sutileza aqui: o fundo do editor é #222222, mas o preto ANSI é #1a1a1a. Isso preserva a convenção de cores do terminal sem transformar toda a interface em preto absoluto.
Ter uma fonte de verdade reduz divergências. Se eu alterar o amarelo de aviso, sei que preciso revisar o amarelo ANSI e a documentação da paleta. Esse cuidado é mais simples do que tentar descobrir, meses depois, por que o editor e o terminal usam cores quase iguais.
O que eu faria desde o início em um próximo tema
Se fosse começar novamente, eu adotaria estes hábitos desde o primeiro commit:
- Criar uma paleta com nomes de papel, não apenas uma lista de hexadecimais.
- Preparar arquivos de exemplo com várias linguagens e estados de interface.
- Testar acessibilidade e contraste antes de considerar uma cor definitiva.
- Documentar a relação entre editor e terminal desde cedo.
- Registrar mudanças visuais no changelog, incluindo o motivo da alteração.
Também vale testar o tema localmente por meio de um link simbólico na pasta de extensões do VS Code. Esse ciclo curto — editar, recarregar a janela, observar — tornou os ajustes muito mais rápidos.
Conclusão
Criar o Underground foi uma forma excelente de estudar ferramentas de desenvolvimento por outro ângulo. Um tema não altera a lógica do programa, mas influencia diretamente a experiência de ler, navegar e depurar código por horas.
A maior lição foi esta: consistência é mais valiosa do que quantidade de cores. Quando fundo, contraste, estados e semântica contam a mesma história, o resultado é mais calmo e mais útil.
Se você quiser explorar a implementação, contribuir ou adaptar a paleta, o código está no repositório do Underground, sob licença MIT.
Top comments (0)