CSS and JavaScript APIs that shipped quietly but deserve your attention—from state-preserving DOM moves to auto-sizing form fields.
The web platform is evolving faster than ever. Between the hype around AI tooling and framework updates, some genuinely useful browser features have shipped without much fanfare.
I've been digging through the Interop 2025 focus areas and recent browser release notes, and I found five features that are production-ready now but flying under most developers' radars.
Let's fix that.
1. moveBefore() — Finally, True DOM Movement
Support: Chrome 133+, Firefox 144+, Safari in development
Ever tried to move a playing video or an iframe to a different part of the page? If you used appendChild() or insertBefore(), you watched helplessly as the video restarted, focus was lost, and animations reset.
That's because the DOM never had a move primitive—only remove and insert. Until now.
// Old way: state is destroyed
newParent.insertBefore(element, referenceNode);
// New way: state is preserved ✨
newParent.moveBefore(element, referenceNode);
The moveBefore() method preserves:
- Animation and transition progress
- Focus and active states
- Fullscreen mode
- Popover open/close state
- Modal
<dialog>state
Real-world use case: Building a responsive layout where a video player moves from a sidebar to the main content area on mobile—without interrupting playback.
if ('moveBefore' in Element.prototype) {
mainContent.moveBefore(videoPlayer, null);
} else {
// Fallback for older browsers
mainContent.appendChild(videoPlayer);
}
2. CSS Custom Highlight API — Highlighting Without DOM Surgery
Support: Baseline as of June 2025 (Chrome, Edge, Safari, Firefox 140+)
Building a search feature? Syntax highlighter? You've probably wrapped matches in <mark> or <span> elements. It works, but you're mutating the DOM, triggering layout recalculations, and wrestling with edge cases when highlights span multiple elements.
The Custom Highlight API takes a completely different approach: define text ranges in JavaScript, style them in CSS, and the DOM stays untouched.
// Find text and create a range
const range = new Range();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);
// Register the highlight
const highlight = new Highlight(range);
CSS.highlights.set('search-result', highlight);
::highlight(search-result) {
background-color: #fef08a;
color: #1e1e1e;
}
Why this matters:
- Zero DOM mutations = zero layout thrashing
- Multiple ranges can share one highlight name
- Highlights can span across element boundaries
- Perfect for search-as-you-type UIs
Pro tip: A single Highlight object can hold multiple Range instances, so you can highlight all search matches with one CSS rule.
3. field-sizing: content — Auto-Growing Form Fields in One Line
Support: Chrome 123+, Edge, Safari 26.2+
Remember writing JavaScript to auto-resize textareas? Or using the size attribute and hoping for the best? Those days are over.
textarea, input, select {
field-sizing: content;
}
That's it. Your form fields now grow and shrink to fit their content.
For production use, you'll want constraints:
textarea {
field-sizing: content;
min-block-size: 3lh; /* At least 3 lines */
max-block-size: 12lh; /* Cap at 12 lines */
}
input {
field-sizing: content;
min-inline-size: 10ch;
max-inline-size: 40ch;
}
Works with:
<textarea>-
<input>(text, email, url, etc.) -
<select>(both dropdown and multi-select)
Progressive enhancement: Browsers that don't support it simply show fixed-size fields. No JavaScript fallback needed.
4. sibling-index() and sibling-count() — CSS Finally Knows Position
Support: Chrome 138+, Firefox in development
How many times have you done this?
{items.map((item, index) => (
<div style={{ '--index': index }}>{item}</div>
))}
Just to create staggered animations. Now CSS can figure out the index itself:
.card {
animation: fade-in 0.3s ease-out backwards;
animation-delay: calc(sibling-index() * 100ms);
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(10px); }
}
And sibling-count() tells you the total number of siblings:
/* Equal-width columns without knowing the count */
.tab {
width: calc(100% / sibling-count());
}
Creative use cases:
- Staggered animations (obviously)
- Dynamic color gradients across items
- Responsive grid layouts based on item count
- Circular positioning with trigonometry
/* Rainbow gradient across any number of items */
.item {
--hue: calc(360 / sibling-count() * (sibling-index() - 1));
background: hsl(var(--hue) 70% 60%);
}
5. CSS Anchor Positioning — Tooltips Without JavaScript
Support: Chrome 125+, Safari 26+, Firefox expected late 2025
If you've ever used Floating UI, Popper.js, or Tippy.js just to position a tooltip, you know the pain. Now CSS handles it natively.
.trigger {
anchor-name: --my-trigger;
}
.tooltip {
position: fixed;
position-anchor: --my-trigger;
position-area: top;
}
The tooltip automatically positions itself relative to the trigger. But here's the magic: it flips automatically when it would overflow the viewport.
Even better—Chrome 133 introduced implicit anchoring for popovers:
<button popovertarget="tip">Hover me</button>
<div id="tip" popover>I'm anchored automatically!</div>
[popover] {
position-area: bottom span-left;
margin: 0;
}
No anchor-name needed. The browser knows the button triggers the popover and creates the relationship automatically.
Which Should You Adopt First?
My recommendation: Start with field-sizing: content.
Here's why:
- Zero risk — Unsupported browsers just show normal fixed fields
- Zero JavaScript — Delete your auto-resize handlers
- Immediate UX win — Users notice when forms feel responsive
- One line of CSS — Lowest effort, highest impact
After that, anchor positioning is your next big win if you're building any kind of tooltip, dropdown, or popover UI.
Wrapping Up
The web platform is getting genuinely better at things we've been solving with JavaScript for years. State-preserving moves, auto-sizing fields, and declarative positioning aren't just nice-to-haves—they're more performant, more accessible, and more maintainable than the JS equivalents.
The features in this post are shipping in stable browsers right now. No flags, no polyfills required (though fallbacks are smart for moveBefore() and the sibling functions).
What overlooked feature are you most excited to try? Let me know in the comments 👇
Top comments (0)