CSS can now do things that used to need JavaScript libraries, build tools, or hundreds of lines of manual DOM code. Most of these features have shipped in the last 2-3 years — but a lot of developers haven't caught up yet.
I built 10 interactive demos to show what's possible. Every demo runs in a live code editor — edit the code and see results instantly. No signup, no install.
1. Scroll Progress Bar — No JavaScript
A reading progress bar that tracks scroll position using animation-timeline: scroll(). No event listeners, no requestAnimationFrame.
.progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #9F7AEA, #ED8936);
transform-origin: left;
animation: grow-bar linear;
animation-timeline: scroll();
}
@keyframes grow-bar {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
What it replaces: scroll event listener + requestAnimationFrame + manual percentage math.
Browser support: Chrome 115+ (Jul 2023), Edge 115+, Safari 18+ (Sep 2024). Firefox: behind flag.
2. Scroll Reveal Animations — No JavaScript
Cards fade in and slide up as they enter the viewport using animation-timeline: view().
.card {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes reveal {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
What it replaces: AOS.js (14KB), GSAP ScrollTrigger, or a custom IntersectionObserver.
Browser support: Chrome 115+ (Jul 2023), Edge 115+, Safari 18+ (Sep 2024). Firefox: behind flag.
3. Anchor-Positioned Popovers — No JavaScript
Position popovers relative to any trigger element using CSS anchor-name and anchor() functions.
.trigger { anchor-name: --btn; }
[popover] {
position-anchor: --btn;
top: anchor(bottom);
left: anchor(left);
margin-top: 8px;
}
@starting-style {
.popover:popover-open {
opacity: 0;
transform: translateY(-8px);
}
}
What it replaces: Popper.js (8KB), Floating UI, or manual getBoundingClientRect() — 200+ lines of JS.
Browser support: Chrome 125+ (May 2024), Edge 125+, Safari 18+ (Sep 2024). Firefox: behind flag.
4. Form Validation Styles with :has()
The form border, button state, and labels react to input validity in real time — zero JavaScript.
/* Green border when all inputs valid */
.form:has(input:valid:not(:placeholder-shown)):not(:has(input:invalid:not(:placeholder-shown))) {
border-color: #22c55e;
}
/* Dim submit when invalid */
.form:has(input:invalid:not(:placeholder-shown)) .submit {
opacity: 0.5;
pointer-events: none;
}
What it replaces: input event listeners on every field + classList toggling + validity checking.
Browser support: Chrome 105+ (Aug 2022), Safari 15.4+ (Mar 2022), Firefox 121+ (Dec 2023). Fully cross-browser.
5. Container Queries — Component-Level Responsive
The same card switches layout based on its container width — not the viewport.
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 420px) {
.card {
grid-template-columns: 180px 1fr;
grid-template-rows: none;
}
}
What it replaces: ResizeObserver for component-level responsiveness. Media queries only check viewport width.
Browser support: Chrome 105+ (Aug 2022), Safari 16+ (Sep 2022), Firefox 110+ (Feb 2023). Fully cross-browser.
6. Animated Accordion — No JavaScript
Smooth open/close on native <details> elements. The browser animates between height: 0 and height: auto.
body {
interpolate-size: allow-keywords;
}
.content {
height: 0;
overflow: hidden;
opacity: 0;
transition: height 0.4s ease, opacity 0.4s ease;
}
details[open] .content {
height: auto;
opacity: 1;
}
What it replaces: Measuring scrollHeight + requestAnimationFrame. Or Headless UI, Radix Accordion, React Collapse.
Browser support: Chrome 129+ (Sep 2024), Edge 129+. Safari and Firefox: not yet supported. Progressive enhancement — accordion still works, just without animation.
7. Native CSS Nesting — Drop Your Preprocessor
The & selector works like Sass — child rules, pseudo-classes, modifiers, all nested in one block.
.card {
background: #1e1b2e;
padding: 24px;
border-radius: 12px;
& h3 { color: #c4b5fd; }
& p { color: #94a3b8; }
&:hover {
transform: translateY(-3px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
&.featured {
border-left: 3px solid #7c3aed;
}
}
What it replaces: Sass/SCSS preprocessor + node-sass/dart-sass dependency + build step. If you only used Sass for nesting, delete the dependency.
Browser support: Chrome 112+ (Apr 2023), Safari 16.5+ (May 2023), Firefox 117+ (Aug 2023). Fully cross-browser.
8. Dynamic Color Palette with color-mix()
Derive an entire color system from one base color. color-mix() in OKLCH space — lighter, darker, and transparent variants from one variable.
:root {
--brand: oklch(0.65 0.25 275);
--brand-light: color-mix(in oklch, var(--brand), white 30%);
--brand-dark: color-mix(in oklch, var(--brand), black 25%);
--brand-subtle: color-mix(in oklch, var(--brand), transparent 85%);
}
.btn { background: var(--brand); }
.btn:hover { background: var(--brand-light); }
What it replaces: Sass lighten()/darken()/mix(), JS color libraries, or manually hardcoded shades.
Browser support: Chrome 111+ (Mar 2023), Safari 16.2+ (Dec 2022), Firefox 113+ (May 2023). Fully cross-browser.
9. View Transitions — State Morphing
Toggle between grid and list layout with a smooth morph. JS only toggles a class — the animation is pure CSS.
toggle.addEventListener('click', () => {
document.startViewTransition(() => {
container.classList.toggle('grid-view');
container.classList.toggle('list-view');
});
});
.item-1 { view-transition-name: item-1; }
.item-2 { view-transition-name: item-2; }
::view-transition-group(*) { animation-duration: 0.35s; }
What it replaces: FLIP animations, React Transition Group, Framer Motion, or manual position calculations.
Browser support: Chrome 111+ (Mar 2023), Safari 18+ (Sep 2024), Firefox 144+ (Oct 2025). Cross-browser since late 2025.
10. Entry Animations from display: none
Animate elements from display: none using @starting-style. Dialogs fade in, cards slide up — no JS timing hacks.
.dialog {
opacity: 1;
transform: scale(1) translateY(0);
transition: opacity 0.3s ease, transform 0.3s ease,
display 0.3s allow-discrete, overlay 0.3s allow-discrete;
}
.dialog:not([open]) {
opacity: 0;
transform: scale(0.9) translateY(10px);
}
@starting-style {
.dialog[open] {
opacity: 0;
transform: scale(0.9) translateY(10px);
}
}
What it replaces: The setTimeout(0) or double requestAnimationFrame hack — set display, wait a frame, add the animation class.
Browser support: Chrome 117+ (Sep 2023), Safari 17.5+ (May 2024), Firefox 129+ (Aug 2024). Fully cross-browser.
Browser Support Cheat Sheet
| Feature | Chrome | Safari | Firefox | Cross-browser? |
|---|---|---|---|---|
| Scroll-driven animations | 115+ (2023) | 18+ (2024) | Behind flag | Partial |
| Anchor positioning | 125+ (2024) | 18+ (2024) | Behind flag | Partial |
:has() |
105+ (2022) | 15.4+ (2022) | 121+ (2023) | Yes |
| Container queries | 105+ (2022) | 16+ (2022) | 110+ (2023) | Yes |
interpolate-size |
129+ (2024) | Not yet | Not yet | Chromium only |
| CSS nesting | 112+ (2023) | 16.5+ (2023) | 117+ (2023) | Yes |
color-mix() |
111+ (2023) | 16.2+ (2022) | 113+ (2023) | Yes |
| View Transitions | 111+ (2023) | 18+ (2024) | 144+ (2025) | Yes |
@starting-style |
117+ (2023) | 17.5+ (2024) | 129+ (2024) | Yes |
Try the Editor
Every demo runs in BigDevSoon's free code editor. HTML, CSS, JavaScript, React, Vue, TypeScript, SCSS, npm packages — all in the browser. No signup required.
Which demo surprised you the most? Drop it in the comments.
Top comments (0)