The future of CSS
is already here.
Every major CSS feature landing in 2027 — container queries, anchor positioning, scroll-driven animations — with live demos and full browser support tables.
What's New in CSS 2027
CSS 2027 consolidates years of specification work into stable, interoperable features across all major engines. From native scoping to physics-aware animations, this is the most significant CSS release in a decade.
Container Queries
Style elements based on their container's size — not the viewport.
Cascade Layers
Explicit control over cascade precedence with @layer.
Color Level 5
Relative colors, color-mix(), and wide-gamut OKLCH spaces.
Native CSS Nesting
Nest selectors inside parents — no preprocessor needed.
@scope
Limit style rules to a specific DOM subtree with a lower boundary.
Anchor Positioning
Position floated elements relative to any anchor — zero JS.
View Transitions
Animate between DOM states and cross-page navigations.
:has() Selector
The long-awaited parent selector — style based on descendants.
Scroll-Driven Animations
Tie animation progress to scroll position — no JS required.
@property
Typed, animatable custom properties with inheritance control.
CSS Subgrid
Align nested grid children on the parent's track definitions.
Math Functions
sin(), cos(), round(), pow(), sqrt(), and more — in native CSS.
Logical Properties
Writing-mode-aware layout that works across LTR and RTL.
@starting-style
Define entry animation states — animate from display:none without JS.
interpolate-size
Animate to and from intrinsic sizes like height: auto natively.
text-wrap: balance
Balance heading line lengths and prevent orphaned words — pure CSS.
Browser Support Matrix
As of mid-2026, all major features are shipping in Chromium. Firefox and Safari are close behind. The table below reflects stable releases, not experimental flags.
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Container Queries | 105+ | 110+ | 16+ | 105+ |
Cascade Layers @layer |
99+ | 97+ | 15.4+ | 99+ |
Color Level 5 color-mix() |
111+ | 113+ | 16.2+ | 111+ |
| Native CSS Nesting | 112+ | 117+ | 17.2+ | 112+ |
@scope |
118+ | Partial | 17.4+ | 118+ |
| Anchor Positioning | 125+ | Flag | Preview | 125+ |
| View Transitions API | 111+ | 128+ | 18+ | 111+ |
:has() Selector |
105+ | 103+ | 15.4+ | 105+ |
| Scroll-Driven Animations | 115+ | 110+ | Preview | 115+ |
@property |
85+ | 128+ | 16.4+ | 85+ |
| CSS Subgrid | 117+ | 71+ | 16+ | 117+ |
Math Functions sin()/cos() |
111+ | 108+ | 15.4+ | 111+ |
| Logical Properties | 89+ | 41+ | 15+ | 89+ |
@starting-style |
117+ | 129+ | 17.5+ | 117+ |
interpolate-size |
129+ | Flag | Preview | 129+ |
text-wrap: balance |
114+ | 121+ | 17.5+ | 114+ |
Container Queries
StableApply styles based on a parent container's size instead of the viewport. This enables truly portable, self-contained components that adapt to any context they're placed in.
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 380px) {
.card {
display: flex;
gap: 1rem;
}
.card__image {
width: 110px;
flex-shrink: 0;
}
}
@container card (max-width: 379px) {
.card {
display: block;
}
.card__image {
width: 100%;
height: 120px;
}
}
Resize the browser to see the card adapt:
container-type: inline-size on the wrapper. Name containers with
container-name to scope queries to specific ancestors. Containers can be
nested.Cascade Layers
Stable@layer gives you explicit control over the cascade. Later-declared
layers always win over earlier ones, regardless of selector specificity — ending the specificity war
for good.
/* Declare order — last wins */
@layer base, components, utilities;
@layer base {
.button {
background: gray;
padding: .5rem 1rem;
color: white;
}
}
@layer components {
.button {
background: blue;
border-radius: .5rem;
}
}
@layer utilities {
/* Wins — even at low specificity */
.button-hot {
background: hotpink;
}
}
Layer order: base → components → utilities
The purple button uses a class
from @layer utilities
— overriding blue without specificity hacks.
Color Level 5
StableCSS Color Level 5 introduces color-mix(), relative color syntax, and
wide-gamut spaces like oklch() for perceptually uniform, design-token-friendly color
systems.
/* Blend two colors */
.mixed {
background: color-mix(in oklch, blue 40%, white);
}
/* Relative color syntax */
:root { --brand: oklch(55% .22 264); }
.lighter {
background: oklch(from var(--brand) calc(l + .2) c h);
}
/* Wide-gamut OKLCH palette */
:root {
--primary: oklch(55% .22 264);
--primary-lg: oklch(70% .22 264);
--primary-dk: oklch(35% .22 264);
}
/* Display-P3 vivid colors */
.vivid { color: color(display-p3 0 0.5 1); }
Native CSS Nesting
StableNest selectors directly inside parent rules — the same syntax you know from SASS, now a native CSS standard. No preprocessor, no build step, just CSS.
.nav {
display: flex;
gap: 1rem;
background: #0f172a;
padding: .75rem 1rem;
border-radius: .75rem;
& a {
color: #94a3b8;
text-decoration: none;
transition: color .2s;
&:hover { color: #818cf8; }
&.active {
color: white;
font-weight: 600;
}
}
/* Responsive — nested media query */
@media (max-width: 600px) {
flex-direction: column;
}
}
Hover links to see &:hover
styles applied.
@scope
Chrome 118+@scope restricts style rules to a specific DOM subtree. You can even
define a lower boundary — a "scope limit" — so styles never leak into nested child components.
/* Rules apply only inside .card */
@scope (.card) {
p {
color: #60a5fa;
font-size: .875rem;
}
h3 { color: white; font-weight: 700; }
}
/* Scope with a lower boundary:
stops before entering .child-component */
@scope (.card) to (.child-component) {
a {
color: #818cf8;
text-decoration: underline;
}
}
Scoped Card
This paragraph is styled by @scope(.sc-card).
This paragraph is outside the scope — unstyled.
Anchor Positioning
Chrome 125+Position absolutely-placed elements relative to any anchor in the DOM — without JavaScript. Perfect for tooltips, dropdowns, and context menus that need to stay tethered to their trigger.
.anchor-btn {
anchor-name: --my-btn;
}
.tooltip {
position: absolute;
position-anchor: --my-btn;
top: anchor(top);
left: anchor(center);
translate: -50% calc(-100% - 8px);
background: #1e293b;
color: white;
padding: .25rem .75rem;
border-radius: .5rem;
font-size: .75rem;
white-space: nowrap;
}
Tooltip tethered via CSS — no JS scroll listeners.
position-try-fallbacks — automatically flip the tooltip if it overflows the
viewport.View Transitions API
StableAnimate between DOM states smoothly. JavaScript triggers the transition with
document.startViewTransition(), and CSS controls the animation via
::view-transition-old/new pseudo-elements.
::view-transition-old(root) {
animation: 300ms ease-out slide-out;
}
::view-transition-new(root) {
animation: 300ms ease-in slide-in;
}
@keyframes slide-out {
to { opacity: 0; transform: translateY(-16px); }
}
@keyframes slide-in {
from { opacity: 0; transform: translateY(16px); }
}
/* Per-element named transitions */
.hero { view-transition-name: hero; }
CSS animation demo. Real View Transitions require Chrome 111+.
:has() Selector
StableThe :has() pseudo-class lets you style a parent based on its
descendants — the "parent selector" the web has wanted for decades. It's one of the most powerful
CSS selectors ever added.
/* Style a card that contains an image */
.card:has(img) {
grid-template-rows: 200px 1fr;
}
/* Strike through li when checkbox checked */
li:has(input[type="checkbox"]:checked) {
text-decoration: line-through;
opacity: .5;
}
/* Float-label pattern — no JS */
.field:has(input:not(:placeholder-shown)) label {
color: #818cf8;
transform: translateY(-1.5rem) scale(.85);
}
/* Dark sibling that follows a .featured card */
.card:has(+ .card.featured) {
filter: brightness(.8);
}
Check items to see :has() apply styles:
- Container Queries docs
- Test anchor positioning
- Write scroll-driven demo
- Explore color-mix()
Scroll-Driven Animations
Chrome 115+Link an animation's progress directly to the scroll position using
animation-timeline. Works for both global scroll and element visibility — zero JS
required.
/* Reading progress bar */
#progress-bar {
position: fixed;
top: 0; left: 0;
height: 3px; background: #5b8dee;
transform-origin: left;
animation: scaleX linear;
animation-timeline: scroll(root block);
}
@keyframes scaleX {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* Fade in as element enters view */
.reveal {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
@keyframes fade-in {
from { opacity: 0; translate: 0 30px; }
to { opacity: 1; translate: 0 0; }
}
@property — Typed Custom Properties
Stable@property registers a custom property with a specific syntax type,
initial value, and inheritance setting. This enables smooth animation of values that were previously
unanimatable — like gradients.
@property --gradient-angle {
syntax: '';
initial-value: 0deg;
inherits: false;
}
.spinning-gradient {
background: conic-gradient(
from var(--gradient-angle),
#5b8dee, #8b5cf6,
#ec4899, #5b8dee
);
animation: spin 3s linear infinite;
}
@keyframes spin {
to { --gradient-angle: 360deg; }
}
/* Typed integer counter */
@property --counter {
syntax: '';
initial-value: 0;
inherits: false;
}
.count { animation: count-up 2s ease-out; }
@keyframes count-up { to { --counter: 100; } }
Animated via @property --rot-angle: <angle>
CSS Subgrid
StableSubgrid lets nested grid children participate in the parent grid's track definitions. Card footers align perfectly across cards of different content lengths — no flexbox hacks needed.
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto;
gap: 1rem;
}
.card {
display: grid;
grid-row: span 3;
/* Inherit parent row tracks */
grid-template-rows: subgrid;
}
/* Inner elements now align across cards */
.card__title { grid-row: 1; }
.card__body { grid-row: 2; }
.card__footer { grid-row: 3; align-self: end; }
Footers align at the bottom regardless of body height.
Math Functions
StableCSS 2027 expands the math suite with round(), mod(),
sin(), cos(), atan2(), pow(), and
sqrt() — enabling true computational layouts without JavaScript.
/* Fluid typography */
.fluid { font-size: clamp(1rem, 2.5vw + .5rem, 2rem); }
/* Snap to 4px grid */
.snapped {
width: round(nearest, 37px, 4px); /* → 36px */
}
/* Circular layout — no JS */
.orbit-item {
--angle: 45deg;
--r: 80px;
translate:
calc(cos(var(--angle)) * var(--r))
calc(sin(var(--angle)) * var(--r));
}
/* Fluid columns without media queries */
.auto-grid {
grid-template-columns:
repeat(auto-fill, minmax(min(260px, 100%), 1fr));
}
/* pow() and sqrt() */
.golden { width: calc(pow(1.618, 3) * 1rem); }
.diagonal { height: calc(sqrt(pow(3, 2) + pow(4, 2)) * 20px); }
Positioned using cos() and sin() — pure CSS
Logical Properties
StableLogical properties decouple layout from physical directions. Using
inline and block instead of left/right/top/bottom makes your CSS work
correctly in LTR, RTL, and vertical writing modes.
/* Physical → Logical mapping:
margin-left → margin-inline-start
margin-right → margin-inline-end
margin-top → margin-block-start
width → inline-size
height → block-size
*/
.card {
margin-inline: auto; /* centered */
padding-block: 1.5rem;
padding-inline: 1rem;
border-inline-start: 3px solid #5b8dee;
max-inline-size: 600px;
}
.scrollable {
block-size: 200px;
overflow-block: auto; /* = overflow-y */
}
/* Works identically in RTL */
[dir="rtl"] .card {
/* border-inline-start auto-flips to right */
}
border-inline-start
renders on the left.border-inline-start
auto-flips to the right.@starting-style
Chrome 117+@starting-style defines an element's styles before its first
paint, enabling smooth entry animations from display: none — no JavaScript, no class
toggling, no requestAnimationFrame hacks required.
/* Animate dialog entry — zero JS */
dialog {
opacity: 1;
translate: 0 0;
transition:
opacity .3s ease,
translate .3s ease,
display .3s ease allow-discrete;
@starting-style {
opacity: 0;
translate: 0 -20px;
}
}
dialog[open] { display: block; }
/* Toast notification */
.toast {
opacity: 1;
transform: translateX(0);
transition: opacity .4s, transform .4s,
display .4s allow-discrete;
@starting-style {
opacity: 0;
transform: translateX(100%);
}
}
.toast.hidden {
display: none;
opacity: 0;
transform: translateX(100%);
}
@starting-style
Each click removes and re-adds the element, triggering @starting-style.
transition: display allow-discrete to also animate the exit state when the element is hidden with display: none.interpolate-size
Chrome 129+Set interpolate-size: allow-keywords on :root to unlock
smooth transitions to and from intrinsic sizing keywords like height: auto,
max-content, and fit-content — the accordion animation devs have wanted for years.
/* Opt in once at the root */
:root {
interpolate-size: allow-keywords;
}
/* Accordion: animate to height: auto */
.accordion-body {
height: 0;
overflow: hidden;
transition: height .4s ease;
}
.accordion-body.open {
height: auto; /* now animatable! */
}
/* Any intrinsic keyword works */
.chip {
width: fit-content;
transition: width .3s ease;
}
.chip:hover {
width: max-content;
}
/* Details element native accordion */
details {
height: 2rem;
overflow: hidden;
transition: height .4s ease;
&[open] { height: auto; }
}
This accordion animates using only CSS transitions.
text-wrap: balance & pretty
StableTwo values that eliminate the most common typographic headaches. balance
evens out line lengths in short headings; pretty prevents orphaned words in long body
text — both with zero JavaScript.
/* balance: even line lengths in headings */
h1, h2, h3 {
text-wrap: balance;
/* Limit: ~6 lines (Chromium), 10 lines (Firefox) */
}
/* pretty: prevent orphans in body text */
p, .prose {
text-wrap: pretty;
}
/* Shorthand */
.hero-title {
text-wrap: balance;
/* Previously required JS + ResizeObserver! */
}
/* text-wrap is now a shorthand for: */
.full-control {
text-wrap-mode: wrap; /* wrap | nowrap */
text-wrap-style: balance; /* auto | balance | pretty | stable */
}
balance on headings (up to 6 lines) and pretty on paragraphs. text-wrap: pretty has a small performance cost, so apply it selectively.Try it in the Playground
Write HTML and CSS side-by-side and see results update in real time. Load any of the 2027 feature snippets and edit live.
Open Playground