
Quando o browser lê seu CSS, ele não o armazena como texto — ele o processa numa estrutura de dados em memória chamada CSSOM. É essa estrutura que o JavaScript manipula quando você escreve element.style.color = 'red'. Entender o CSSOM é entender como o browser pensa sobre estilo.
O que é o CSSOM
O CSSOM (CSS Object Model) é o contraparte CSS do DOM. Assim como o DOM representa o HTML em forma de árvore de nós que o JavaScript pode ler e modificar, o CSSOM representa as regras CSS em uma estrutura hierárquica análoga.
Quando o browser encontra uma folha de estilo — seja via <link>, <style> ou element.style — ele faz o parse das regras e constrói o CSSOM. Cada regra vira um objeto com propriedades como selectorText, cssText e acesso individual às declarações.
A combinação de DOM + CSSOM gera a Render Tree: apenas os nós visíveis, já com seus estilos calculados. É sobre a Render Tree que o browser executa Layout, Paint e Composite.
Árvore de nós gerada a partir do HTML. Representa a estrutura e o conteúdo da página.
Árvore de regras gerada a partir do CSS. Representa os estilos calculados para cada seletor.
DOM + CSSOM → Render Tree → Layout → Paint → Composite
Inspecionando o CSSOM
Cada propriedade que você vê no DevTools em "Computed" vem do CSSOM calculado. O browser resolve herança, cascata e especificidade e entrega um valor final para cada propriedade de cada elemento.
<button class="cta">Clique aqui</button>
const el = document.querySelector('.cta');
getComputedStyle(el).backgroundColor; // "rgb(203, 166, 247)"
el.style.backgroundColor; // "" (inline style, empty)Dois conceitos importantes:
- Computed styles (
getComputedStyle(el)): o valor final resolvido, após cascata e herança. Sempre retorna um valor absoluto — nuncaemou%, semprepxourgb(). - Inline styles (
el.style): apenas os estilos aplicados diretamente no atributostyledo elemento. Se o estilo vem de uma classe ou herança,el.style.propertyretorna string vazia.
Cada valor tem uma origem (source): pode vir de uma regra de classe, de herança de um ancestral, ou do stylesheet padrão do browser (user-agent). A origem e a especificidade determinam qual regra vence quando há conflito.
Manipulando o CSSOM com JavaScript
Existem três formas principais de alterar estilos via JavaScript, cada uma com tradeoffs diferentes:
const box = document.querySelector('.box'); box.style.backgroundColor = '#cba6f7'; box.style.transform = 'scale(1.05)'; box.style.transition = 'all 0.3s ease';
const box = document.querySelector('.box');
box.style.backgroundColor = '#cba6f7';
box.style.transform = 'scale(1.05)';
box.style.transition = 'all 0.3s ease';element.style — estilos inline, altíssima especificidade, afeta apenas aquele elemento. Bom para animações ou estilos calculados em runtime.
classList.toggle/add/remove — mantém a lógica de estilo no CSS, o JS só controla qual classe está ativa. É a abordagem mais limpa para estados (hover, active, open, disabled).
CSSStyleSheet.insertRule — insere regras diretamente no stylesheet. Útil quando você precisa aplicar estilos a muitos elementos de uma vez sem poluir o HTML com classes ou inline styles. É como o browser aplica @keyframes gerados dinamicamente.
Performance: reflow e repaint
Nem toda mudança CSS tem o mesmo custo. O browser tem um pipeline de renderização, e dependendo do que você muda, pode ser necessário recalcular tudo do zero ou apenas repintar alguns pixels.
- Compositor apenas (GPU) — transform, opacity
O browser não precisa recalcular layout nem repintar. A mudança acontece direto no compositor, na GPU. Custo praticamente zero. Use para animações.
- Repaint — color, background-color, box-shadow
O layout não muda (nenhum elemento se move ou muda de tamanho), mas o browser precisa repintar os pixels afetados. Custo médio.
- Reflow (layout) — width, height, margin, padding, top, left
O browser precisa recalcular a posição e o tamanho de todos os elementos afetados, depois repintar. Custo alto, especialmente em árvores grandes.
Animar top ou left dispara reflow a cada frame. Animar transform: translate() roda inteiramente na GPU sem tocar no layout. O resultado visual é o mesmo, mas o custo de performance é ordens de magnitude menor.
Outro padrão que causa problemas: ler e escrever estilos alternadamente no mesmo frame (layout thrashing). O browser é obrigado a recalcular o layout a cada leitura se houve uma escrita antes.
// Ruim — layout thrashing
box.style.width = box.offsetWidth + 10 + 'px'; // write → read → write
// Bom — agrupa leituras antes das escritas
const w = box.offsetWidth; // read
box.style.width = w + 10 + 'px'; // writeO CSSOM no Critical Rendering Path
O CSSOM é render-blocking: o browser não pode construir a Render Tree — e portanto não pode pintar nada — enquanto não terminar de baixar e processar todo o CSS. Por isso arquivos CSS pesados atrasam o First Contentful Paint.
O caminho completo:
- DOM — parse do HTML, construção da árvore de nós
- CSSOM — parse das folhas de estilo, resolução de cascata e herança
- Render Tree — combina DOM + CSSOM, inclui apenas nós visíveis
- Layout — calcula posição e tamanho de cada elemento
- Paint — rasteriza pixels em camadas
- Composite — une as camadas e envia para a tela
CSS inline (<style>) é processado mais rápido porque já está no HTML e não gera um request extra. CSS externo deve ser servido com cabeçalhos de cache adequados e, se possível, o CSS crítico (above-the-fold) deve ser inlined.
Resumo
Assim como o DOM, o CSSOM representa o CSS em memória como uma estrutura hierárquica que o JavaScript pode ler e modificar.
getComputedStyle retorna o valor final resolvido (sempre absoluto). element.style retorna apenas estilos inline — vazio se o estilo vem de uma classe.
element.style para runtime dinâmico, classList para estados, CSSStyleSheet.insertRule para regras programáticas em larga escala.
Animações com transform e opacity rodam no compositor (GPU) sem reflow. São sempre a melhor escolha para performance.
O browser não pinta nada até processar todo o CSS. CSS crítico deve ser pequeno e servido rapidamente para um bom FCP.
Não alterne leituras e escritas de layout no mesmo frame. Agrupe todas as leituras antes de fazer as escritas.
As minhas notas
Selecione um trecho e use Destacar no menu (somente neste aparelho).