Modern CSS has evolved dramatically over the past few years, shipping features that previously required JavaScript or preprocessors. Here's a look at some of the most impactful additions.
#Container Queries
For years, responsive design meant reacting to the viewport width. Container queries change that by letting elements respond to the size of their parent container instead.
1.card-wrapper {
2 container-type: inline-size;
3 container-name: card;
4}
5
6@container card (min-width: 400px) {
7 .card {
8 display: grid;
9 grid-template-columns: 1fr 2fr;
10 }
11}
This is a fundamental shift — a component can now be truly self-contained and layout-aware, regardless of where it's placed on the page.
Browser support: All major browsers as of late 2023.
#The :has() Selector
Often called the "parent selector," :has() lets you style an element based on what it contains.
1/* Style a form group that contains an invalid input */
2.form-group:has(input:invalid) {
3 border-color: red;
4}
5
6/* Style a card that has an image */
7.card:has(img) {
8 padding: 0;
9}
10
11/* Style a label whose sibling input is checked */
12label:has(+ input:checked) {
13 font-weight: bold;
14}
Before :has(), achieving this required JavaScript to add and remove classes manually. It's one of the most powerful selectors ever added to CSS.
Browser support: All major browsers as of December 2023.
#CSS Nesting
Native CSS nesting, inspired by Sass, is now supported without a build step.
1.button {
2 background: blue;
3 color: white;
4
5 &:hover {
6 background: darkblue;
7 }
8
9 &.disabled {
10 opacity: 0.5;
11 pointer-events: none;
12 }
13
14 .icon {
15 margin-right: 8px;
16 }
17}
The & refers to the parent selector. Nesting without & (like .icon above) is also valid and targets descendants.
Browser support: All major browsers as of late 2023.
#CSS Cascade Layers
@layer gives you explicit control over the cascade, solving the specificity arms race in large codebases.
1@layer reset, base, components, utilities;
2
3@layer reset {
4 * { box-sizing: border-box; margin: 0; }
5}
6
7@layer base {
8 a { color: blue; }
9}
10
11@layer utilities {
12 .mt-4 { margin-top: 1rem; }
13}
Styles in a later layer always win over styles in an earlier layer, regardless of specificity. Styles outside any layer beat all layered styles.
This makes third-party CSS, design tokens, and utility classes much easier to manage without resorting to !important.
Browser support: All major browsers since 2022.
#The color-mix() Function
Mix two colors directly in CSS without a preprocessor.
1:root {
2 --brand: #3b82f6;
3 --brand-light: color-mix(in srgb, var(--brand) 30%, white);
4 --brand-dark: color-mix(in srgb, var(--brand) 70%, black);
5}
6
7.button {
8 background: var(--brand);
9}
10
11.button:hover {
12 background: var(--brand-dark);
13}
The in srgb part specifies the color space. You can also use in oklch, which produces more perceptually uniform results — particularly useful for generating accessible color scales.
Browser support: All major browsers since 2023.
#Logical Properties
Logical properties replace physical directions (left, right, top, bottom) with writing-mode-relative ones (inline-start, inline-end, block-start, block-end). This makes layouts work correctly in right-to-left and vertical writing modes without extra overrides.
1/* Instead of this */
2.element {
3 margin-left: 1rem;
4 padding-top: 2rem;
5 border-right: 1px solid;
6}
7
8/* Use this */
9.element {
10 margin-inline-start: 1rem;
11 padding-block-start: 2rem;
12 border-inline-end: 1px solid;
13}
Shorthand versions are also available:
1.element {
2 margin-inline: 1rem; /* left and right */
3 padding-block: 2rem; /* top and bottom */
4 inset-inline: 0; /* left: 0; right: 0 */
5}
Browser support: All major browsers, widely available since 2021–2022.
#@property — Custom Property Types
@property lets you define typed CSS custom properties with default values and inheritance control. The most powerful use case is animating properties that CSS wouldn't otherwise know how to interpolate.
1@property --gradient-angle {
2 syntax: "<angle>";
3 initial-value: 0deg;
4 inherits: false;
5}
6
7.spinner {
8 background: conic-gradient(from var(--gradient-angle), blue, red, blue);
9 animation: spin 2s linear infinite;
10}
11
12@keyframes spin {
13 to { --gradient-angle: 360deg; }
14}
Without @property, animating a custom property inside a gradient would not work — the browser wouldn't know it's an angle. Declaring the type makes the interpolation possible.
Browser support: All major browsers since 2023.
#What's Coming Next
Part 2 will cover:
@starting-style— animate elements as they enter the DOManchor()positioning — tether floating elements to any anchorlight-dark()— inline light/dark theming without media queries- Scroll-driven animations — tie animation progress to scroll position, no JavaScript needed
All browser support notes refer to evergreen browsers as of early 2025. For production use, always verify on caniuse.com.