CSS moderno sem framework: container queries, layers e nesting
1. Introdução ao CSS moderno sem dependências
Em 2025, o ecossistema CSS nativo atingiu um nível de maturidade que torna questionável a dependência exclusiva de frameworks como Bootstrap ou Tailwind. Três recursos — container queries, layers e nesting — resolvem problemas históricos de responsividade, especificidade e organização de código sem a necessidade de bibliotecas externas.
Container queries permitem que componentes respondam ao espaço disponível do seu container pai, não apenas à viewport. Layers oferecem controle explícito sobre a cascata, eliminando guerras de especificidade. Nesting nativo traz a sintaxe de aninhamento que antes exigia Sass ou Less. Juntos, formam um trio poderoso para escrever CSS modular, performático e de fácil manutenção.
Todos os navegadores modernos suportam esses recursos desde 2023-2024, com cobertura acima de 95% globalmente. Você pode usá-los em produção hoje.
2. Container Queries: design responsivo por componente
O conceito central das container queries é inverter a lógica de responsividade. Em vez de perguntar "qual o tamanho da tela?", perguntamos "qual o tamanho do container pai?". Isso permite que um mesmo componente se adapte a diferentes contextos de layout.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
A propriedade container-type define o eixo de dimensionamento (inline-size para largura, size para ambos os eixos). container-name nomeia o container para consultas específicas.
3. Trabalhando com container queries na prática
Um cenário comum é uma grid de produtos onde cada card precisa se reorganizar conforme o espaço disponível no container, não na viewport.
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
.product-card {
container-type: inline-size;
container-name: product;
padding: 1rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
@container product (min-width: 300px) {
.product-card {
display: grid;
grid-template-columns: 120px 1fr;
gap: 1rem;
}
.product-image {
width: 100%;
height: auto;
}
.product-details {
display: flex;
flex-direction: column;
justify-content: center;
}
}
@container product (max-width: 299px) {
.product-card {
display: flex;
flex-direction: column;
text-align: center;
}
.product-image {
max-width: 150px;
margin: 0 auto;
}
}
Combinar container queries com media queries é estratégia inteligente: use media queries para o layout macro (grid) e container queries para o micro-layout dos componentes.
4. CSS Layers: organização e controle de especificidade
Layers resolvem o problema clássico de "quem ganha na cascata". Com @layer, você define a ordem de precedência explicitamente, independentemente da ordem de importação ou especificidade dos seletores.
@layer reset, base, components, utilities;
@layer reset {
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
color: #333;
}
h1, h2, h3 {
line-height: 1.2;
color: #111;
}
}
@layer components {
.button {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
background: #0066cc;
color: white;
cursor: pointer;
}
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
.text-center { text-align: center; }
.bg-gray { background: #f5f5f5; }
}
A ordem declarada em @layer reset, base, components, utilities define que reset tem menor prioridade e utilities a maior. Isso significa que uma classe utilitária sempre sobrescreverá um estilo de componente, mesmo que a especificidade seja idêntica.
5. Estratégias avançadas com layers
Para projetos maiores, você pode importar arquivos dentro de layers, mantendo a organização modular.
/* main.css */
@layer base, layout, components, utilities;
@layer base {
@import 'reset.css';
@import 'typography.css';
}
@layer layout {
@import 'grid.css';
@import 'header.css';
@import 'footer.css';
}
@layer components {
@import 'buttons.css';
@import 'cards.css';
@import 'forms.css';
}
@layer utilities {
@import 'spacing.css';
@import 'colors.css';
}
Layers podem ser aninhados para controle ainda mais granular:
@layer base {
@layer reset, tokens, typography;
@layer reset {
/* estilos de reset */
}
@layer tokens {
/* variáveis CSS */
}
}
A grande vantagem: você elimina a necessidade de convenções como BEM ou especificidade artificial para garantir que utilitários sobrescrevam componentes.
6. Nesting nativo: aninhamento CSS sem pré-processadores
O nesting nativo do CSS segue a especificação do W3C e é mais flexível que o Sass. A sintaxe básica usa & para referenciar o seletor pai, mas em muitos casos é opcional.
.card {
padding: 1rem;
border-radius: 8px;
background: white;
/* Aninhamento sem & para seletores descendentes */
.card-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.card-body {
color: #666;
line-height: 1.5;
}
/* Aninhamento com & para pseudo-classes */
&:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
&::before {
content: '';
display: block;
height: 2px;
background: linear-gradient(90deg, #0066cc, #00cc66);
}
/* Media queries aninhadas */
@media (max-width: 600px) {
padding: 0.75rem;
.card-title {
font-size: 1rem;
}
}
}
Diferenças importantes do Sass: no CSS nativo, & sempre se refere ao seletor pai completo, não há concatenação implícita. Para seletores como .card.dark, você escreve &.dark. Seletores compostos funcionam naturalmente.
7. Combinando as três técnicas em um projeto real
Vamos unir tudo em um exemplo prático: um dashboard com cards de métricas que se adaptam ao container.
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer base {
body {
font-family: system-ui, sans-serif;
background: #f0f2f5;
padding: 2rem;
}
}
@layer components {
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
container-type: inline-size;
container-name: dashboard;
}
.metric-card {
container-type: inline-size;
container-name: metric;
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
.metric-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
.metric-icon {
width: 40px;
height: 40px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.metric-label {
font-size: 0.875rem;
color: #666;
text-transform: uppercase;
letter-spacing: 0.05em;
}
}
.metric-value {
font-size: 2rem;
font-weight: 700;
color: #111;
}
.metric-change {
font-size: 0.875rem;
margin-top: 0.5rem;
&.positive { color: #00cc66; }
&.negative { color: #ff4444; }
}
@container metric (max-width: 300px) {
padding: 1rem;
.metric-header {
flex-direction: column;
text-align: center;
}
.metric-value {
font-size: 1.5rem;
text-align: center;
}
.metric-change {
text-align: center;
}
}
@container metric (min-width: 301px) and (max-width: 450px) {
.metric-header {
.metric-icon {
width: 32px;
height: 32px;
font-size: 1rem;
}
}
.metric-value {
font-size: 1.75rem;
}
}
}
}
@layer utilities {
.bg-blue { background: #e8f0fe; }
.bg-green { background: #e6f7ed; }
.bg-orange { background: #fef3e2; }
}
A estrutura de layers garante que reset, base e componentes tenham prioridades claras. O nesting mantém o código legível e coeso. As container queries fazem cada card se adaptar ao espaço disponível no grid.
8. Considerações finais e próximos passos
O CSS moderno sem frameworks elimina a necessidade de bibliotecas externas para 80% dos casos de uso. O impacto no bundle é zero — tudo é nativo do navegador. A performance melhora porque não há runtime de framework CSS interpretando classes utilitárias.
Para migrar gradualmente de Bootstrap ou Tailwind:
1. Comece substituindo o sistema de grid por CSS Grid nativo com container queries
2. Migre componentes um a um, usando layers para isolar estilos novos dos antigos
3. Adote nesting gradualmente, refatorando arquivos conforme tocar neles
Ferramentas como o DevTools do Chrome já oferecem debugging visual para container queries e layers. A especificação W3C continua evoluindo, com propostas como @scope e @when no horizonte.
O futuro do CSS é nativo, modular e sem dependências. Container queries, layers e nesting são a base dessa nova era.
Referências
- MDN Web Docs: CSS Container Queries — Documentação completa sobre container queries, com exemplos interativos e tabela de compatibilidade.
- W3C Specification: CSS Cascading and Inheritance Level 5 (Layers) — Especificação oficial dos CSS Layers, incluindo regras de precedência e exemplos formais.
- CSS Nesting Module Level 1 (W3C Working Draft) — Especificação oficial do nesting nativo, com detalhes sobre sintaxe e diferenças em relação a pré-processadores.
- Chrome Developers: Container Queries: a practical guide — Guia prático do Google sobre container queries, com casos de uso reais e dicas de performance.
- Smashing Magazine: A Complete Guide To CSS Layers — Artigo aprofundado sobre estratégias de organização com layers, incluindo exemplos de projetos reais.
- CSS-Tricks: Nesting in CSS — Tutorial detalhado sobre nesting nativo, com comparações entre sintaxe Sass e CSS puro.