Novo Livro!
Categorias
Projetos
CSS

Substituindo funções de cor do Sass por CSS nativo

Substituindo funções de cor do Sass por CSS nativo

12 anos atrás, no artigo Colour me Sass-y, Stuart Robson apresentou uma série de helpers de cor do Sass que tornavam a estilização diária rápida e prática.

Naquela época, o Sass era o caminho mais fácil. Em 2026, o CSS moderno oferece muito mais poder nativo de manipulação de cores do que havia em 2014.

tl;dr

Se o fluxo de trabalho com Sass depende muito de helpers de cor, estas são as principais substituições nativas em CSS:

  • rgba($color, 0.4)rgb(from var(--color) r g b / 40%)
  • lighten($color, 20%)hsl(from var(--color) h s calc(l + 20)) (paridade com Sass)
  • darken($color, 20%)hsl(from var(--color) h s calc(l - 20)) (paridade com Sass)
  • tint($color, 20%)color-mix(in srgb, var(--color) 80%, white 20%) (paridade com Sass)
  • shade($color, 20%)color-mix(in srgb, var(--color) 80%, black 20%) (paridade com Sass)
  • adjust-hue($color, 40deg)hsl(from var(--color) calc(h + 40) s l)
  • grayscale($color)hsl(from var(--color) h 0% l)
  • complement($color)hsl(from var(--color) calc(h + 180) s l)

lighten()/darken() e tint()/shade() não são a mesma operação no Sass. lighten()/darken() ajustam a luminosidade HSL, enquanto tint()/shade() fazem mistura com branco/preto.

Substituições adicionais:

  • saturate($color, 20%)hsl(from var(--color) h calc(s + 20) l)
  • desaturate($color, 20%)hsl(from var(--color) h calc(s - 20) l)

Por que isso funciona melhor agora

O Sass trouxe manipulação de cores muito antes dos navegadores; agora o CSS nativo alcançou esse patamar de duas formas importantes:

  1. color-mix() oferece mistura confiável para tints, shades e blending.
  2. A sintaxe de cores relativas (rgb(from ...), hsl(from ...)) permite derivar novas cores a partir de tokens existentes.

Juntas, essas funcionalidades abrem um fluxo de trabalho onde uma única cor de origem pode gerar múltiplos estados e temas.

Uma cor de origem

A ideia é manter uma cor principal da marca como custom property CSS e derivar tudo a partir dela.

Uma boa regra geral: usar OKLCH para os tokens e OKLab para as misturas. OKLCH — lightness, chroma, hue — é fácil de ler e ajustar manualmente. OKLab mistura de forma mais limpa, sem mudanças inesperadas de matiz no meio do caminho.

:root {
--brand: oklch(62% 0.19 26);
/* Superfícies e ênfase */
--brand-soft: color-mix(in oklab, var(--brand), white 88%);
--brand-strong: color-mix(in oklab, var(--brand), black 22%);
/* Estados */
--brand-hover: color-mix(in oklab, var(--brand), black 12%);
--brand-active: color-mix(in oklab, var(--brand), black 24%);
--brand-border: color-mix(in oklab, var(--brand), black 35%);
/* Variantes com alfa */
--brand-alpha-08: rgb(from var(--brand) r g b / 8%);
--brand-alpha-18: rgb(from var(--brand) r g b / 18%);
}

Com essa abordagem, não se está mais armazenando dez valores hex sem relação entre si. Os valores são derivados de uma única custom property, o que torna refatoração e tematização muito mais fáceis. Inclusive, esse é o tipo de técnica que pode ser aproveitada para criar dark mode com CSS de forma muito mais simples.

Substituindo funções comuns de cor do Sass

rgba()

A versão clássica do Sass:

$brand: #d13400;
background-color: rgba($brand, 20%);

Equivalente em CSS nativo:

:root {
--brand: #d13400;
}
.alert {
background-color: rgb(from var(--brand) r g b / 20%);
}

lighten() e darken()

Ambas são nativas do Sass e ajustam a luminosidade HSL diretamente:

lighten(#6b717f, 20%);
darken(#6b717f, 20%);

Equivalente em CSS nativo usando sintaxe de cores relativas:

:root {
--brand: #6b717f;
--brand-lighten-20: hsl(from var(--brand) h s calc(l + 20));
--brand-darken-20: hsl(from var(--brand) h s calc(l - 20));
}

Para resultados com aparência mais natural, pode-se usar OKLCH:

:root {
--brand: #6b717f;
--brand-lighten-20: oklch(from var(--brand) calc(l + 0.2) c h);
--brand-darken-20: oklch(from var(--brand) calc(l - 0.2) c h);
}

tint() e shade()

Estas não eram nativas do Sass, eram comumente escritas como helpers personalizados ou obtidas de uma biblioteca:

@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}

Equivalente em CSS nativo usando color-mix(). Pode-se usar srgb para paridade com o Sass, ou trocar para oklab para misturas com melhor aparência perceptual:

:root {
--brand: #6b717f;
/* srgb: mais próximo da saída do Sass */
--brand-tint-20: color-mix(in srgb, var(--brand) 80%, white 20%);
--brand-shade-20: color-mix(in srgb, var(--brand) 80%, black 20%);
/* oklab: melhor mistura perceptual */
--brand-tint-20: color-mix(in oklab, var(--brand) 80%, white 20%);
--brand-shade-20: color-mix(in oklab, var(--brand) 80%, black 20%);
}

adjust-hue() e complement()

As versões do Sass:

adjust-hue(#d13400, 40deg);
complement(#d13400);

Equivalente em CSS nativo usando sintaxe de cores relativas em hsl():

:root {
--brand: #d13400;
--brand-shift-40: hsl(from var(--brand) calc(h + 40) s l);
--brand-complement: hsl(from var(--brand) calc(h + 180) s l);
}

Demonstração de adjust-hue():

Demonstração de complement():

grayscale()

A versão do Sass:

grayscale(#d13400);

Equivalente em CSS nativo:

:root {
--brand: #d13400;
--brand-gray: hsl(from var(--brand) h 0% l);
}

saturate() e desaturate()

As versões do Sass:

saturate(#d13400, 20%);
desaturate(#d13400, 20%);

Equivalente em CSS nativo:

:root {
--brand: #d13400;
--brand-more-sat: hsl(from var(--brand) h calc(s + 20) l);
--brand-less-sat: hsl(from var(--brand) h calc(s - 20) l);
}

Um exemplo prático de componente

Aqui está uma receita simples de botão que obtém todos os seus estados de cor a partir de um único token.

:root {
--btn: oklch(58% 0.2 28);
--btn-text: white;
--btn-hover: color-mix(in oklab, var(--btn), black 10%);
--btn-active: color-mix(in oklab, var(--btn), black 18%);
--btn-ring: rgb(from var(--btn) r g b / 35%);
--btn-subtle-bg: color-mix(in oklab, var(--btn), white 90%);
}
.button {
background: var(--btn);
color: var(--btn-text);
border: 1px solid color-mix(in oklab, var(--btn), black 30%);
box-shadow: 0 0 0 0 var(--btn-ring);
}
.button:hover {
background: var(--btn-hover);
}
.button:active {
background: var(--btn-active);
}
.button:focus-visible {
box-shadow: 0 0 0 0.25rem var(--btn-ring);
}
.button--subtle {
background: var(--btn-subtle-bg);
color: color-mix(in oklab, var(--btn), black 45%);
}

Esse é um conjunto completo de estados a partir de uma única cor de origem, sem necessidade de funções Sass.

Progressive enhancement e fallbacks

As funções de cor modernas têm excelente suporte nos navegadores, mas se for necessário dar suporte a navegadores mais antigos, será preciso usar fallbacks. A estratégia é direta: fornecer uma cor estática primeiro, depois aprimorar com @supports.

Fallback básico com @supports

.badge {
/* Fallback: cor estática que funciona em qualquer lugar */
background: #d13400;
color: white;
}
@supports (color: color-mix(in oklab, red, white)) {
/* Aprimoramento: cores derivadas para navegadores modernos */
.badge {
background: color-mix(in oklab, #d13400, white 15%);
color: color-mix(in oklab, #d13400, black 55%);
}
}

Suporte dos navegadores

color-mix: color-mix() permite misturar duas cores com um peso especificado, sendo essencial para criar tints, shades e variações de cor personalizadas sem Sass.

Sintaxe de cores relativas: a sintaxe de cores relativas permite derivar novas cores a partir de existentes usando funções como hsl(from var(--color) ...), possibilitando transformações de cor dinâmicas diretamente no CSS.

Conclusão

O Sass ainda é uma ferramenta válida e ainda é utilizado por muitos. Mas, especificamente para trabalho com cores, o CSS nativo passou de “quase lá” para “muito utilizável”.

Se o projeto pode contar com suporte a navegadores modernos, já é possível fazer bastante manipulação de cores diretamente em CSS. Ou seja, dá para “des-Sassificar” o CSS um pouco mais.

Para quem quer se aprofundar, vale conferir também o artigo sobre o que é possível colocar em uma variável CSS e a discussão sobre se variáveis CSS são melhores que loops de Sass.