As propriedades CSS personalizadas evoluíram muito além da simples troca de cores. Este guia explora os padrões avançados que tornam as propriedades CSS um dos recursos mais poderosos do CSS moderno.
Fundamentos das propriedades personalizadas
As propriedades personalizadas são definidas com o prefixo -- e acessadas com a função var().
/* CSS Custom Properties — Fundamentals */
/* Define properties on :root for global scope */
:root {
--color-primary: #0066cc;
--color-primary-dark: #0052a3;
--spacing-unit: 8px;
--font-size-base: 1rem;
--border-radius: 4px;
}
/* Use with var() */
.button {
background-color: var(--color-primary);
padding: calc(var(--spacing-unit) * 1.5) calc(var(--spacing-unit) * 3);
border-radius: var(--border-radius);
font-size: var(--font-size-base);
}
/* Override at component scope (not global) */
.button.danger {
--color-primary: #cc0000; /* Only affects this button */
--color-primary-dark: #990000;
}
/* Fallback value (second argument to var) */
.card {
background: var(--card-bg, white);
color: var(--card-text, var(--color-text, #333)); /* nested fallback */
padding: var(--card-padding, var(--spacing-unit, 8px));
}Sistemas de theming com design tokens
O uso mais poderoso das propriedades personalizadas é construir um sistema completo de design tokens.
/* Multi-Theme System with CSS Custom Properties */
/* Light theme (default) */
:root {
--color-bg: #ffffff;
--color-surface: #f8f9fa;
--color-border: #e2e8f0;
--color-text-primary: #1a202c;
--color-text-secondary: #718096;
--color-accent: #3182ce;
--color-accent-hover: #2c5282;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition-fast: 150ms ease;
}
/* Dark theme — override only what changes */
[data-theme="dark"] {
--color-bg: #1a202c;
--color-surface: #2d3748;
--color-border: #4a5568;
--color-text-primary: #f7fafc;
--color-text-secondary: #a0aec0;
--color-accent: #63b3ed;
--color-accent-hover: #90cdf4;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
}
/* High-contrast theme */
[data-theme="high-contrast"] {
--color-bg: #000000;
--color-text-primary: #ffffff;
--color-accent: #ffff00;
--color-border: #ffffff;
}
/* System preference auto-detection */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-bg: #1a202c;
--color-text-primary: #f7fafc;
/* ... more overrides */
}
}
/* Components use semantic tokens — never raw colors */
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
color: var(--color-text-primary);
box-shadow: var(--shadow-md);
transition: box-shadow var(--transition-fast);
}Variantes de componentes com propriedades personalizadas
As propriedades personalizadas habilitam sistemas de variantes de componentes mais flexíveis.
/* Component Variant System */
/* Button component with custom property API */
.btn {
/* Default values (the component's "API") */
--btn-bg: var(--color-accent);
--btn-bg-hover: var(--color-accent-hover);
--btn-color: white;
--btn-border: transparent;
--btn-shadow: var(--shadow-sm);
--btn-size: 1rem;
--btn-padding-y: 0.5em;
--btn-padding-x: 1em;
--btn-radius: var(--border-radius);
background: var(--btn-bg);
color: var(--btn-color);
border: 1px solid var(--btn-border);
box-shadow: var(--btn-shadow);
font-size: var(--btn-size);
padding: var(--btn-padding-y) var(--btn-padding-x);
border-radius: var(--btn-radius);
transition: background var(--transition-fast);
}
.btn:hover {
background: var(--btn-bg-hover);
}
/* Variants — only override what changes */
.btn-danger {
--btn-bg: #e53e3e;
--btn-bg-hover: #c53030;
}
.btn-outline {
--btn-bg: transparent;
--btn-bg-hover: var(--color-accent);
--btn-color: var(--color-accent);
--btn-border: var(--color-accent);
}
.btn-sm {
--btn-size: 0.875rem;
--btn-padding-y: 0.375em;
--btn-padding-x: 0.75em;
}
.btn-lg {
--btn-size: 1.125rem;
--btn-padding-y: 0.75em;
--btn-padding-x: 1.5em;
}
/* Caller overrides — no need for a new variant class */
.special-hero .btn {
--btn-bg: gold;
--btn-color: black;
--btn-radius: 50px;
}Interoperabilidade JavaScript
As propriedades personalizadas conectam CSS e JavaScript perfeitamente.
// JavaScript <-> CSS Custom Properties
// 1. Read a custom property value
const root = document.documentElement;
const primaryColor = getComputedStyle(root)
.getPropertyValue('--color-primary')
.trim();
console.log(primaryColor); // '#0066cc'
// 2. Set a custom property from JavaScript
root.style.setProperty('--color-primary', '#ff6600');
// 3. Remove a custom property
root.style.removeProperty('--color-primary');
// 4. Theme switcher
function setTheme(theme) {
document.documentElement.dataset.theme = theme;
localStorage.setItem('theme', theme);
}
// 5. Animate with custom properties (requires @property)
// CSS:
// @property --progress {
// syntax: '<number>';
// initial-value: 0;
// inherits: false;
// }
// .progress-bar {
// width: calc(var(--progress) * 1%);
// transition: --progress 1s ease;
// }
// JavaScript:
function animateProgress(el, from, to) {
el.style.setProperty('--progress', from);
requestAnimationFrame(() => {
el.style.setProperty('--progress', to);
});
}
// 6. Reactive properties with ResizeObserver
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
entry.target.style.setProperty('--el-width', `${width}px`);
entry.target.style.setProperty('--el-height', `${height}px`);
}
});
observer.observe(document.querySelector('.responsive-component'));Perguntas frequentes
As propriedades CSS personalizadas são iguais às variáveis SASS?
Não, são fundamentalmente diferentes. As variáveis SASS são compiladas em tempo de build.
As propriedades CSS personalizadas podem ser animadas?
Sim, com @property (Houdini).
O que é o valor de fallback em var()?
O segundo argumento de var() é um fallback usado quando a propriedade não está definida.
Funcionam em todos os navegadores modernos?
Sim, suporte completo desde 2017.