Variáveis CSS: por que é importante?

As variáveis CSS, mais precisamente conhecidas como propriedades CSS personalizadas, estão chegando ao Chrome 49. Elas podem ser úteis para reduzir a repetição no CSS e também para efeitos poderosos no tempo de execução, como a troca de tema e a possível extensão/polyfill de futuros recursos de CSS.

Excesso de CSS

Ao projetar um aplicativo, é comum reservar um conjunto de cores da marca que serão reutilizadas para manter a aparência do app consistente. Infelizmente, repetir esses valores de cor repetidamente no CSS não é apenas uma tarefa, mas também pode causar erros. Se, em algum momento, uma das cores precisar ser alterada, você pode se descuidar e usar o recurso "encontrar e substituir" em tudo, mas, em um projeto grande, isso pode se tornar perigoso.

Recentemente, muitos desenvolvedores passaram a usar pré-processadores de CSS, como SASS ou LESS, que resolvem esse problema com o uso de variáveis de pré-processador. Embora essas ferramentas tenham aumentado imensamente a produtividade dos desenvolvedores, as variáveis que elas usam têm uma grande desvantagem: elas são estáticas e não podem ser modificadas no momento da execução. Adicionar a capacidade de mudar variáveis no momento de execução não apenas abre a porta para coisas como temas de aplicativos dinâmicos, mas também tem ramificações importantes para o design responsivo e o potencial de preencher futuros recursos do CSS. Com o lançamento do Chrome 49, essas capacidades agora estão disponíveis na forma de propriedades personalizadas do CSS.

Propriedades personalizadas em poucas palavras

As propriedades personalizadas adicionam dois novos recursos à nossa caixa de ferramentas do CSS:

  • A capacidade de um autor atribuir valores arbitrários a uma propriedade com um nome escolhido pelo autor.
  • A função var(), que permite que um autor use esses valores em outras propriedades.

Confira um exemplo rápido para demonstrar

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color é uma propriedade personalizada definida pelo autor com um valor de #06c. Todas as propriedades personalizadas começam com dois traços.

A função var() recupera e se substitui pelo valor da propriedade personalizada, resultando em color: #06c;. Contanto que a propriedade personalizada seja definida em algum lugar da folha de estilo, ela estará disponível para a função var.

A sintaxe pode parecer um pouco estranha no início. Muitos desenvolvedores perguntam: "Por que não usar $foo para nomes de variáveis?" A abordagem foi escolhida especificamente para ser o mais flexível possível e permitir macros $foo no futuro. Para saber mais sobre o histórico, leia esta postagem de um dos autores da especificação, Tab Atkins.

Sintaxe de propriedades personalizadas

A sintaxe de uma propriedade personalizada é simples.

--header-color: #06c;

As propriedades personalizadas diferenciam maiúsculas de minúsculas, então --header-color e --Header-Color são propriedades personalizadas diferentes. Embora pareçam simples, a sintaxe permitida para propriedades personalizadas é bastante permissiva. Por exemplo, a propriedade personalizada a seguir é válida:

--foo: if(x > 5) this.width = 10;

Embora isso não seja útil como uma variável, porque seria inválido em qualquer propriedade normal, ele poderia ser lido e usado com JavaScript no tempo de execução. Isso significa que as propriedades personalizadas têm o potencial de desbloquear todos os tipos de técnicas interessantes que não são possíveis atualmente com os pré-processadores de CSS atuais. Então, se você está pensando "Bocaaaaa eu tenho SASS, então quem se importa…", dê uma segunda olhada! Essas não são as variáveis com que você está acostumado a trabalhar.

A cascata

As propriedades personalizadas seguem as regras de cascata padrão. Assim, você pode definir a mesma propriedade em diferentes níveis de especificidade.

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

Isso significa que você pode aproveitar propriedades personalizadas em consultas de mídia para ajudar com o design responsivo. Um caso de uso pode ser expandir a margem em torno dos principais elementos de seção à medida que o tamanho da tela aumenta:

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

É importante ressaltar que o snippet de código acima não pode ser usado com os pré-processadores de CSS atuais, que não conseguem definir variáveis dentro de consultas de mídia. Ter essa capacidade libera muito potencial.

Também é possível ter propriedades personalizadas que derivam o valor de outras propriedades personalizadas. Isso pode ser extremamente útil para temas:

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

Função var()

Para recuperar e usar o valor de uma propriedade personalizada, você precisa usar a função var(). A sintaxe da função var() é assim:

var(<custom-property-name> [, <declaration-value> ]? )

Em que <custom-property-name> é o nome de uma propriedade personalizada definida pelo autor, como --foo, e <declaration-value> é um valor alternativo a ser usado quando a propriedade personalizada referenciada for inválida. Os valores substitutos podem ser uma lista separada por vírgulas, que será combinada em um único valor. Por exemplo, var(--font-stack, "Roboto", "Helvetica"); define um substituto de "Roboto", "Helvetica". Lembre-se de que os valores abreviados, como os usados para margem e padding, não são separados por vírgulas. Portanto, um substituto adequado para padding ficaria assim.

p {
    padding: var(--pad, 10px 15px 20px);
}

Usando esses valores substitutos, o autor do componente pode escrever estilos defensivos para o elemento:

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

Essa técnica é especialmente útil para temas de componentes da Web que usam o Shadow DOM, já que as propriedades personalizadas podem atravessar os limites de sombra. Um autor de componente da Web pode criar um design inicial usando valores padrão e expor "hooks" de temas na forma de propriedades personalizadas.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

Ao usar var(), há alguns problemas a serem observados. As variáveis não podem ser nomes de propriedades. Por exemplo:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

No entanto, isso não é equivalente a definir margin-top: 20px;. Em vez disso, a segunda declaração é inválida e é descartada como um erro.

Da mesma forma, não é possível (de forma simples) criar um valor em que parte dele seja fornecida por uma variável:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

Novamente, isso não é equivalente a definir margin-top: 20px;. Para criar um valor, você precisa de outra coisa: a função calc().

Como criar valores com calc()

Se você nunca trabalhou com ela antes, a função calc() é uma ferramenta prática que permite realizar cálculos para determinar valores de CSS. Ele é compatível com todos os navegadores modernos e pode ser combinado com propriedades personalizadas para criar novos valores. Exemplo:

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

Como trabalhar com propriedades personalizadas em JavaScript

Para receber o valor de uma propriedade personalizada no momento da execução, use o método getPropertyValue() do objeto CSSStyleDeclaration calculado.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

Da mesma forma, para definir o valor da propriedade personalizada no momento da execução, use o método setProperty() do objeto CSSStyleDeclaration.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

Também é possível definir o valor da propriedade personalizada para se referir a outra propriedade personalizada no momento da execução usando a função var() na chamada para setProperty().

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

Como as propriedades personalizadas podem se referir a outras propriedades personalizadas nas folhas de estilo, é possível imaginar como isso pode levar a todos os tipos de efeitos interessantes no momento de execução.

Suporte ao navegador

Atualmente, o Chrome 49, o Firefox 42, o Safari 9.1 e o Safari para iOS 9.3 oferecem suporte a propriedades personalizadas.

Demonstração

Teste o exemplo para conferir todas as técnicas interessantes que você pode usar com as propriedades personalizadas.

Leitura adicional

Se você quiser saber mais sobre as propriedades personalizadas, Philip Walton, da equipe do Google Analytics, escreveu um guia sobre por que ele está animado com as propriedades personalizadas. Além disso, você pode acompanhar o progresso em outros navegadores em chromestatus.com.