Variáveis CSS: por que é importante?

As variáveis CSS, mais especificamente conhecidas como propriedades personalizadas CSS, estarão no Chrome 49. Eles podem ser úteis para reduzir a repetição no CSS e também para efeitos avançados de tempo de execução, como troca de temas e potencialmente ampliar/polyfilling de recursos CSS futuros.

Desordem de CSS

Ao projetar um aplicativo, é uma prática comum separar um conjunto de cores da marca que serão reutilizadas para manter a aparência do app consistente. Infelizmente, repetir esses valores de cor várias vezes no CSS não é apenas uma tarefa árdua, mas também propensa a erros. Se, em algum momento, você precisar mudar uma das cores, poderia tomar cuidado e “encontrar e substituir” tudo, mas, em um projeto grande o suficiente, isso poderia facilmente se tornar perigoso.

Recentemente, muitos desenvolvedores recorreram a pré-processadores CSS como SASS ou LESS, que resolvem esse problema com variáveis de pré-processador. Embora essas ferramentas tenham aumentado imensamente a produtividade do desenvolvedor, as variáveis usadas sofrem uma grande desvantagem, que é que são estáticas e não podem ser alteradas no ambiente de execução. Adicionar a capacidade de alterar variáveis no tempo de execução não apenas abre as portas para coisas como temas dinâmicos de aplicativos, mas também tem grandes ramificações para o design responsivo e o potencial para polyfill futuro de recursos CSS. Com o lançamento do Chrome 49, esses recursos agora estão disponíveis na forma de propriedades personalizadas CSS.

Resumo das propriedades personalizadas

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

  • 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.

Aqui está 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 na folha de estilo, ela deve 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 apenas $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 a história, leia esta postagem de um dos autores das especificações, Tab Atkins.

Sintaxe da propriedade personalizada

A sintaxe de uma propriedade personalizada é direta.

--header-color: #06c;

As propriedades personalizadas diferenciam maiúsculas de minúsculas. Portanto, --header-color e --Header-Color são propriedades personalizadas diferentes. Embora possam parecer simples, a sintaxe permitida para propriedades personalizadas é bastante permissiva. Por exemplo, o código a seguir é uma propriedade personalizada 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 executado 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 atualmente possíveis com os pré-processadores CSS atuais. Então, se você está pensando "bocejo eu tenho SASS, então quem se importa...", então 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 regras em cascata padrão. Assim, é possível 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 é possível aproveitar propriedades personalizadas dentro das consultas de mídia para ajudar com o design responsivo. Um caso de uso pode ser expandir as margens 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 usando os pré-processadores CSS atuais, que são incapazes de definir variáveis dentro de consultas de mídia. Ter essa habilidade desbloqueia muito potencial!

Também é possível ter propriedades personalizadas que são derivadas de outras propriedades personalizadas. Isso pode ser extremamente útil para aplicação de temas:

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

Função var()

Para recuperar e usar o valor de uma propriedade personalizada, você precisará 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 a ser usado quando a propriedade personalizada referenciada for inválida. Os valores substitutos podem ser uma lista separada por vírgulas, que serão combinadas 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 preenchimento, não são separados por vírgulas. Por isso, um substituto adequado para o preenchimento ficaria assim.

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

Com esses valores substitutos, o autor de um 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 aplicar temas a componentes da Web que usam o DOM de sombra, já que as propriedades personalizadas podem ultrapassar os limites do sombreamento. Um autor de componente da Web pode criar um design inicial usando valores substitutos e expor "ganchos" 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 que precisam ser observados. 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 é gerada como um erro.

Da mesma forma, não é possível criar (simplesmente) 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 ferramenta pequena que permite realizar cálculos para determinar valores de CSS. Ele é compatível com todos os navegadores mais recentes 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 no JavaScript

Para receber o valor de uma propriedade personalizada no ambiente de 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 durante a 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 suas folhas de estilo, você pode imaginar como isso poderia levar a todos os tipos de efeitos interessantes no ambiente de execução.

Suporte ao navegador

No momento, o Chrome 49, o Firefox 42, o Safari 9.1 e o Safari 9.3 no iOS são compatíveis com propriedades personalizadas.

Demonstração

Teste a amostra (em inglês) para ter uma ideia de todas as técnicas interessantes que você pode aproveitar agora graças às propriedades personalizadas.

Leia mais

Para saber mais sobre as propriedades personalizadas, Philip Walton, da equipe do Google Analytics, escreveu uma introdução sobre por que ele está animado com as propriedades personalizadas. Você pode acompanhar o progresso delas em outros navegadores em chromestatus.com.