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.