Modern CSS in 2025: Container Queries, Cascade Layers and New Selectors
Hello HaWkers, CSS has gone through a revolution in recent years. Features that once required JavaScript or were simply impossible are now native. If you're still stuck with 2020 CSS, you're missing incredible tools.
Did you know it's now possible to style an element based on the size of its parent container, not just the viewport? Let's explore the most powerful features of modern CSS.
The State of CSS in 2025
CSS has evolved more in the last 3 years than in the previous decade. Modern browsers now support advanced features with excellent compatibility.
Browser Support in 2025
Features with full support (95%+ browsers):
- Container Queries
- Cascade Layers
- CSS Nesting
- :has() Selector
- Subgrid
- Color functions (oklch, color-mix)
- Scroll-driven animations
- View transitions
Still experimental:
- CSS Anchor Positioning
- Scroll state container queries
Container Queries
Container Queries are probably the most impactful addition to CSS since Flexbox and Grid. They allow styling elements based on the size of the parent container.
Basic Setup
/* Define container */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Or shorthand */
.card-container {
container: card / inline-size;
}
/* Apply styles based on container */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 150px 1fr;
gap: 1rem;
}
.card-image {
width: 150px;
aspect-ratio: 1;
}
}
@container card (min-width: 600px) {
.card {
grid-template-columns: 200px 1fr;
}
.card-title {
font-size: 1.5rem;
}
}Truly Responsive Card Component
/* Container queries for truly responsive cards */
.card-wrapper {
container: card / inline-size;
}
.card {
display: flex;
flex-direction: column;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card-content {
padding: 1rem;
}
.card-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.card-description {
font-size: 0.875rem;
color: #666;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.card-meta {
display: none;
}
/* Medium container: horizontal layout */
@container card (min-width: 350px) {
.card {
flex-direction: row;
}
.card-image {
width: 120px;
aspect-ratio: 1;
}
.card-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
}
/* Large container: more details */
@container card (min-width: 500px) {
.card-image {
width: 180px;
}
.card-title {
font-size: 1.25rem;
}
.card-description {
-webkit-line-clamp: 3;
}
.card-meta {
display: flex;
gap: 1rem;
margin-top: 0.75rem;
font-size: 0.75rem;
color: #888;
}
}
Cascade Layers
Cascade Layers solve one of CSS's biggest problems: managing specificity. They allow defining style layers with controlled precedence.
Defining Layers
/* Define layer order (first has lowest precedence) */
@layer reset, base, components, utilities;
/* Reset styles */
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
}
}
/* Base styles */
@layer base {
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 600;
}
}
/* Components */
@layer components {
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
border-radius: 8px;
border: none;
cursor: pointer;
transition: all 0.2s ease;
}
.button-primary {
background: #0066cc;
color: white;
}
.button-primary:hover {
background: #0055aa;
}
.card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
}
/* Utilities (highest precedence) */
@layer utilities {
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.flex { display: flex; }
.grid { display: grid; }
.hidden { display: none; }
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-4 { margin-top: 1rem; }
.mt-8 { margin-top: 2rem; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
}Importing External CSS into Layers
/* Import external libraries into specific layers */
@import url('https://unpkg.com/normalize.css') layer(reset);
@import url('./vendor/library.css') layer(vendor);
/* Define order including vendor */
@layer reset, vendor, base, components, utilities;
/* Your styles in components will override vendor */
@layer components {
/* Customizations that override the library */
.library-component {
border-radius: 8px; /* Overrides original */
}
}
The :has() Selector
The :has() selector is often called the "parent selector" - something developers have asked for decades.
Practical Use Cases
/* Style parent element based on child */
/* Form with error */
.form-group:has(input:invalid) {
border-color: #dc3545;
}
.form-group:has(input:invalid) .form-label {
color: #dc3545;
}
/* Card with image */
.card:has(img) {
padding: 0;
}
.card:has(img) .card-content {
padding: 1.5rem;
}
/* Card without image */
.card:not(:has(img)) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
/* Navigation with open submenu */
.nav-item:has(.submenu:hover),
.nav-item:has(.submenu:focus-within) {
background: #f5f5f5;
}
.nav-item:has(.submenu:hover) .submenu,
.nav-item:has(.submenu:focus-within) .submenu {
display: block;
}
/* Figure with caption */
figure:has(figcaption) img {
border-radius: 8px 8px 0 0;
}
figure:has(figcaption) figcaption {
background: #f5f5f5;
padding: 0.75rem;
border-radius: 0 0 8px 8px;
}
/* Custom checkbox that affects sibling label */
input[type="checkbox"]:checked + label {
text-decoration: line-through;
color: #999;
}
/* Grid that adapts to content */
.grid-container:has(> :nth-child(4)) {
grid-template-columns: repeat(2, 1fr);
}
.grid-container:has(> :nth-child(7)) {
grid-template-columns: repeat(3, 1fr);
}Smart Forms
/* Show/hide fields based on selections */
.form:has(#option-business:checked) .business-fields {
display: block;
}
.form:has(#option-personal:checked) .business-fields {
display: none;
}
/* Submit button enabled only when form is valid */
.form:has(:invalid) .submit-button {
opacity: 0.5;
pointer-events: none;
}
/* Form progress indicator */
.form-progress::before {
content: '';
width: 0%;
height: 4px;
background: #0066cc;
transition: width 0.3s ease;
}
.form:has(input:nth-of-type(1):valid) .form-progress::before {
width: 25%;
}
.form:has(input:nth-of-type(2):valid) .form-progress::before {
width: 50%;
}
.form:has(input:nth-of-type(3):valid) .form-progress::before {
width: 75%;
}
.form:has(input:nth-of-type(4):valid) .form-progress::before {
width: 100%;
background: #28a745;
}
Native CSS Nesting
We finally have native nesting in CSS, similar to Sass/Less.
Nesting Syntax
/* Native nesting - no preprocessor */
.card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
/* Child elements */
& .card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
& .card-title {
font-size: 1.25rem;
font-weight: 600;
color: #333;
}
& .card-body {
color: #666;
line-height: 1.6;
}
& .card-footer {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 0.5rem;
}
/* Pseudo-classes */
&:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
&:focus-within {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Variants */
&.card-featured {
border: 2px solid #0066cc;
& .card-title {
color: #0066cc;
}
}
&.card-danger {
border-left: 4px solid #dc3545;
& .card-title::before {
content: '⚠️ ';
}
}
/* Nested media queries */
@media (min-width: 768px) {
padding: 2rem;
& .card-title {
font-size: 1.5rem;
}
}
/* Nested container queries */
@container card (min-width: 400px) {
flex-direction: row;
& .card-image {
width: 150px;
}
}
}
Subgrid
Subgrid allows child elements to participate in the parent's grid.
Perfect Card Alignment
/* Main grid */
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
/* Card using subgrid to align content */
.card {
display: grid;
grid-template-rows: auto 1fr auto;
/* Or with subgrid to inherit parent's rows */
/* grid-template-rows: subgrid; */
gap: 1rem;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-image {
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card-content {
padding: 0 1.5rem;
/* Flexible to take available space */
}
.card-footer {
padding: 1rem 1.5rem;
background: #f8f9fa;
display: flex;
justify-content: space-between;
align-items: center;
}
/* Example with real subgrid */
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto 1fr auto;
gap: 2rem;
}
.feature-card {
display: grid;
grid-row: span 4;
grid-template-rows: subgrid;
background: white;
border-radius: 12px;
padding: 1.5rem;
gap: 0;
}
.feature-icon {
/* Row 1: icon */
font-size: 2.5rem;
margin-bottom: 1rem;
}
.feature-title {
/* Row 2: title */
font-size: 1.25rem;
font-weight: 600;
}
.feature-description {
/* Row 3: description (flexible) */
color: #666;
line-height: 1.6;
}
.feature-link {
/* Row 4: link (aligned in all cards) */
margin-top: 1rem;
color: #0066cc;
font-weight: 500;
}
New Color Functions
Modern CSS offers much more powerful color functions.
OKLCH and Color-Mix
:root {
/* OKLCH: better for creating accessible palettes */
--primary: oklch(55% 0.25 250);
--primary-light: oklch(70% 0.2 250);
--primary-dark: oklch(40% 0.25 250);
/* Generating variants automatically */
--primary-hover: oklch(from var(--primary) calc(l - 10%) c h);
--primary-active: oklch(from var(--primary) calc(l - 20%) c h);
/* Color-mix for transparencies */
--primary-10: color-mix(in srgb, var(--primary) 10%, transparent);
--primary-50: color-mix(in srgb, var(--primary) 50%, transparent);
/* Derived semantic colors */
--success: oklch(65% 0.2 145);
--warning: oklch(75% 0.15 85);
--danger: oklch(55% 0.25 25);
}
.button-primary {
background: var(--primary);
color: white;
&:hover {
background: var(--primary-hover);
}
&:active {
background: var(--primary-active);
}
}
/* Automatic dark theme with OKLCH */
@media (prefers-color-scheme: dark) {
:root {
--primary: oklch(70% 0.2 250);
--primary-light: oklch(80% 0.15 250);
--primary-dark: oklch(60% 0.2 250);
}
}
Scroll-Driven Animations
Scroll-based animations, without JavaScript.
/* Define scroll timeline */
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Apply scroll-based animation */
.animate-on-scroll {
animation: fade-in linear;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
/* Parallax with scroll */
@keyframes parallax {
from {
transform: translateY(0);
}
to {
transform: translateY(-100px);
}
}
.parallax-background {
animation: parallax linear;
animation-timeline: scroll();
}
/* Scroll-based progress bar */
.reading-progress {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: #0066cc;
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll();
}
@keyframes grow-progress {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}Conclusion
CSS in 2025 is a completely different language from CSS a few years ago. Container Queries, Cascade Layers, :has() and the other features presented solve problems that once required JavaScript or were impossible.
To start today:
- Try Container Queries in a component
- Organize your styles with Cascade Layers
- Replace JavaScript with :has() where possible
- Adopt CSS Nesting for cleaner code
The web is evolving rapidly, and CSS is keeping up. Developers who master these tools create more elegant interfaces with less code.
If you want to deepen your knowledge in frontend, I recommend checking out another article: React, Vue and Angular in 2025 where you'll discover how these frameworks integrate with modern CSS.
Let's go! 🦅
📚 Want to Deepen Your JavaScript Knowledge?
This article covered modern CSS, but JavaScript remains essential for any frontend developer.
Developers who invest in solid, structured knowledge tend to have more opportunities in the market.
Complete Study Material
If you want to master JavaScript from basics to advanced, I've prepared a complete guide:
Investment options:
- 1x of $4.90 on card
- or $4.90 at sight
👉 Learn About JavaScript Guide
💡 Material updated with industry best practices

