Maps e loops no Sass: @each, @for e utilitários gerados
Como guardar a paleta num map, gerar dezenas de classes com @each e @for, e o que acontece quando a chave pedida não existe.
Map guarda pares de chave e valor; @each percorre coleções; @for percorre
uma faixa numérica. Juntos, eles transformam tokens em CSS. O benefício é uma
fonte de dados única. O risco é gerar centenas de regras que ninguém inspeciona.
Cada exemplo foi enviado ao Dart Sass 1.103.1 pela entrada padrão, sem sourcemap e com estilo expandido. Os casos válidos terminaram com status 0, e as saídas relevantes estão registradas ao lado de cada trecho.
O primeiro map e map.get
Carregue sass:map, declare pares entre parênteses e leia uma chave:
@use "sass:map";
$colors: (
primary: #6d28d9,
success: #16a34a
);
.button { color: map.get($colors, primary); }O map não aparece na saída. Ele é adequado quando a coleção alimenta várias operações ou precisa ser configurada como unidade. Para duas constantes sem relação, variáveis individuais podem ser mais legíveis.
Gerar classes com @each
@each $name, $value in $colors desempacota cada par. Interpolação coloca a
chave no seletor:
$colors: (
primary: #6d28d9,
success: #16a34a,
danger: #dc2626
);
@each $name, $value in $colors {
.text-#{$name} { color: $value; }
}.text-success { color: #16a34a; }
.text-danger { color: #dc2626; }
Três entradas geraram três regras. Antes de ampliar para dez propriedades e vinte cores, defina quais combinações o produto usa. Gerar o produto cartesiano por conveniência aumenta a folha mesmo quando quase todas as classes ficam ociosas.
Maps aninhados para temas
Um valor de map pode ser outro map. Dart Sass atual aceita várias chaves em
map.get, atravessando níveis:
@use "sass:map";
$theme: (
light: (surface: white, text: #111827),
dark: (surface: #111827, text: white)
);
.dark {
background: map.get($theme, dark, surface);
color: map.get($theme, dark, text);
}Para tema que muda no navegador, emita esses valores como custom properties em vez de duplicar cada componente. O map pode ser a fonte do build; a cascata deve continuar responsável pela troca em runtime.
Chave ausente vira null e pode sumir
map.get não lança erro para chave desconhecida. Ele devolve null, e uma
declaração cujo valor é null não é emitida. Como a regra fica vazia, ela também
some:
@use "sass:map";
$colors: (primary: #6d28d9);
.missing { color: map.get($colors, warning); }O compilador terminou com status 0 e não imprimiu CSS. Esse silêncio é conveniente para configuração opcional e perigoso para token obrigatório. Uma classe sem cor pode chegar ao deploy sem warning.
Validar token obrigatório com map.has-key
Envolva o acesso numa função quando ausência for defeito de contrato:
@use "sass:map";
$colors: (primary: #6d28d9);
@function token($name) {
@if not map.has-key($colors, $name) {
@error "Token #{$name} não existe";
}
@return map.get($colors, $name);
}
.alert { color: token(warning); }Agora o build falha perto do consumo e diz qual chave faltou. Teste também a configuração padrão do módulo para não descobrir um token obrigatório apenas em uma aplicação consumidora.
@for through: incluir o último número
Use faixa quando os itens são numéricos e contíguos. through inclui três:
@for $i from 1 through 3 {
.span-#{$i} { grid-column: span $i; }
}.span-2 { grid-column: span 2; }
.span-3 { grid-column: span 3; }
O seletor usa interpolação; o valor mantém o número. Defina um limite ligado ao sistema real de grid, não um valor alto “para garantir”. Cada iteração publica uma classe.
@for to: parar antes do limite
Trocar uma palavra muda a quantidade. to 3 gera um e dois:
@for $i from 1 to 3 {
.step-#{$i} { z-index: $i; }
}.step-2 { z-index: 2; }
Esse detalhe causa lacunas silenciosas. Se o domínio diz “de 1 até 3 inclusive”,
through expressa melhor. Para uma lista de nomes ou valores irregulares,
prefira @each.
Quanto CSS o loop realmente gerou
Para medir em vez de estimar, montei cinco famílias com nove tons cada. As
chaves de família estão entre aspas porque nomes como blue e red também são
cores reconhecidas pelo Sass; assim a interpolação compilou sem warnings.
$palette: (
"violet": (
50: #f5f3ff, 100: #ede9fe, 200: #ddd6fe,
300: #c4b5fd, 400: #a78bfa, 500: #8b5cf6,
600: #7c3aed, 700: #6d28d9, 900: #4c1d95
),
"blue": (
50: #eff6ff, 100: #dbeafe, 200: #bfdbfe,
300: #93c5fd, 400: #60a5fa, 500: #3b82f6,
600: #2563eb, 700: #1d4ed8, 900: #1e3a8a
),
"green": (
50: #f0fdf4, 100: #dcfce7, 200: #bbf7d0,
300: #86efac, 400: #4ade80, 500: #22c55e,
600: #16a34a, 700: #15803d, 900: #14532d
),
"amber": (
50: #fffbeb, 100: #fef3c7, 200: #fde68a,
300: #fcd34d, 400: #fbbf24, 500: #f59e0b,
600: #d97706, 700: #b45309, 900: #78350f
),
"red": (
50: #fef2f2, 100: #fee2e2, 200: #fecaca,
300: #fca5a5, 400: #f87171, 500: #ef4444,
600: #dc2626, 700: #b91c1c, 900: #7f1d1d
)
);
@each $family, $tones in $palette {
@each $tone, $value in $tones {
.bg-#{$family}-#{$tone} {
background-color: $value;
}
}
}Compilei o arquivo em estilo expandido, contei seletores, linhas e bytes, e comprimi pela entrada padrão para o cabeçalho do gzip não carregar nome de arquivo:
npx --no-install sass matrix-5x9.scss matrix-5x9.css --no-source-map --style=expanded
rg -c '^\.bg-' matrix-5x9.css
wc -l -c matrix-5x9.css
gzip -9 -c < matrix-5x9.css | wc -cO Sass 1.103.1 terminou com status 0 e sem warnings. A matriz 5 × 9 publicou 45
seletores em 179 linhas: 2.136 bytes, aproximadamente 2,09 KiB, e 415 bytes com
gzip -9. A primeira regra foi .bg-violet-50; a última,
.bg-red-900. Compressão reduziu a transferência, mas as 45 classes continuam
na cascata mesmo que a página use somente duas.
Use allowlists por propriedade, gere somente a API documentada e meça tamanho bruto e gzip. Se o projeto quer utilitários sob demanda, uma ferramenta que analisa conteúdo pode ser mais adequada que gerar todas as combinações no Sass.
Organizar tokens como API de módulo
Mantenha map privado e exponha funções como token() ou variáveis públicas
selecionadas. Assim, o formato interno pode mudar sem obrigar consumidores a
conhecer níveis e chaves. Combine com @use e
@forward para uma fachada estável.
Defina um schema humano mesmo que Sass não tenha tipos de objeto formais. Documente chaves obrigatórias, unidades aceitas e se mapas do consumidor são substituídos ou mesclados. Uma configuração que aceita qualquer estrutura só transfere o erro para uma chamada distante. Funções de acesso podem validar e emitir mensagem com o caminho procurado.
Ao mesclar temas, escolha precedência explicitamente. Padrão primeiro e sobrescrita depois costuma ser previsível, mas um map aninhado pode exigir merge profundo. Teste uma chave substituída, uma chave nova e uma seção ausente. O CSS final deve mostrar que somente as decisões esperadas mudaram.
Versione a API pelos nomes semânticos, não pela paleta visual. danger pode
mudar de tom sem obrigar componentes a conhecer red-600; já remover danger
quebra o contrato. Se a camada precisa expor escala primitiva e tokens
semânticos, mantenha maps separados e faça componentes consumir o segundo.
Loops merecem testes de contagem. Registre quantas regras uma configuração padrão produz e coloque um limite no CI para detectar expansão acidental. Um novo estado multiplicado por cores, tamanhos e breakpoints pode aumentar a folha em milhares de seletores sem alterar muito o fonte. Métrica torna esse custo visível durante a revisão.
Para depurar, imprima temporariamente valores com @debug e remova a chamada
antes de publicar. Confira chave e tipo no ponto em que entram no loop. Quando o
seletor gerado está errado, investigue interpolação; quando a propriedade some,
investigue null; quando o build explode, leia a função de validação e a pilha.
Revise funções e operações para validar entradas. O guia de Sass ajuda a decidir se a geração traz valor ou apenas recria uma biblioteca de utilitários dentro do projeto.
Perguntas frequentes
Map Sass chega ao navegador?
Qual a diferença entre through e to no @for?
O que map.get devolve para chave ausente?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Dart Sass 1.103.1 (dart2js 3.13.1), Node 26.3.0, macOS, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- Sass — sass:map — sass-lang.com
- Sass — @each — sass-lang.com
- Sass — @for — sass-lang.com


