Variáveis CSS: por que é importante?

Variáveis CSS, conhecidas como propriedades personalizadas de CSS, estão chegando ao Chrome 49. Eles podem ser úteis para reduzir a repetição no CSS e também para efeitos poderosos de tempo de execução, como troca de tema e possivelmente ampliação/polyfill de futuros recursos do CSS.

Desordem de CSS

Ao projetar um app, é uma prática comum separar um conjunto de cores da marca que serão reutilizadas para manter a consistência da aparência do app. 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ê poderá tomar cuidado e "encontrar e substituir" todas as coisas, mas, em um projeto grande o suficiente, isso pode ser facilmente 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 impulsionado imensamente a produtividade do desenvolvedor, as variáveis usadas têm uma grande desvantagem: elas são estáticas e não podem ser alteradas durante a execução. Adicionar a capacidade de alterar variáveis no tempo de execução não apenas abre a porta para coisas como temas dinâmicos de aplicativos, mas também tem grandes ramificações para o design responsivo e o potencial de polyfill de futuros recursos do CSS. Com o lançamento do Chrome 49, esses recursos agora estão disponíveis na forma de propriedades personalizadas de CSS.

Propriedades personalizadas em poucas palavras

As propriedades personalizadas adicionam dois novos recursos à caixa de ferramentas de 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 substitui a si mesma pelo valor da propriedade personalizada, resultando em color: #06c;. Desde que a propriedade personalizada esteja definida em algum lugar da folha de estilo, ela precisa 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 apenas 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 o histórico, leia esta postagem de um dos autores das especificações, 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 possam parecer 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, já que 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 usar todos os tipos de técnicas interessantes que os pré-processadores de CSS atuais não oferecem. 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 as quais você está acostumado.

A cascata

As propriedades personalizadas seguem regras em cascata padrão para que você possa 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 as propriedades personalizadas nas 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 é possível ao usar os pré-processadores de CSS atuais, que são incapazes de 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() é semelhante a esta:

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 substituto usado quando a propriedade personalizada referenciada é 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írgula. Portanto, um substituto apropriado para o 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;
}

É preciso ter cuidado ao usar var(). 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 criar (ingenuamente) um valor em que parte dele é 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().

Criar valores com calc()

Se você nunca trabalhou com ela antes, a função calc() é uma pequena ferramenta que permite realizar cálculos para determinar valores de CSS. Ela é compatível com todos os navegadores mais recentes e pode ser combinada 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 conseguir 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 folha de estilos, é 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 iOS Safari 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.