This content originally appeared on Bram.us and was authored by Bramus!
~
CSS Anchor Positioning is a powerful tool, but one of the things that you cannot do natively (yet) is animating the position-area property. This blog post introduces a technique to animate position-area changes using View Transitions.
~
🌟 This post is about CSS Anchor Positioning. If you are not familiar with the basics of it, check out this 30-min talk of mine to get up to speed.
~
The problem
When the browser chooses one of the position-try-fallbacks to apply to an anchored element, the position-area’s Computed Value changes to that chosen fallback. This change is abrupt and in response the anchored element simply jumps from the old position-area to the new position-area. This change can’t be animated using CSS because position-area is discretely animatable (or rather: it’s currently defined as “TBD”, see w3c/csswg-drafts#13577), so the value just flips midway the transition.
.tooltip {
position: fixed;
position-area: block-start;
position-try-fallbacks: flip-block;
transition: position-area 0.2s ease; /* This doesn’t do anything visually … */
}
~
The technique
Back in 2024 I explored a technique to automatically trigger a View Transition whenever a CSS property changes. For this I relied on @bramus/style-observer which is a StyleObserver that allows you to respond to Computed Value changes in JavaScript. In the StyleObserver’s callback, I reset the targeted element to the previously recorded value, and then start a View Transition to the new value of that property.
For Anchor Positioning specifically, it’s sufficient to monitor the position-area property, as its Computed Value changes whenever a new position-try-fallback gets applied. To make sure that the View Transition is started from the previously recorded position-area, the position-try must be unset temporarily at the start of the View Transition. Therefore, that property also needs to be monitored.
import StyleObserver, { ReturnFormat, NotificationMode } from "@bramus/style-observer";
const $element = document.querySelector('.tooltip');
let isBusy = false;
const styleObserver = new StyleObserver((mutations) => {
// Prevent double runs
if (isBusy) return;
isBusy = true;
const positionArea = mutations['position-area'];
// No change, do nothing
if (!positionArea.previousValue || !positionArea.changed) {
isBusy = false;
return;
}
// Move the element back its old location
// This is done by forcing the old recorded positionArea
// but most importantly by also unsetting the `position-try`
$element.style.positionTry = 'none';
$element.style.positionArea = positionArea.previousValue;
// Restore the new positions
const t = document.startViewTransition(() => {
$element.style.positionTry = '';
$element.style.positionArea = '';
});
isBusy = false;
},
{
properties: ['position-area', 'position-anchor', 'position-try'],
returnFormat: ReturnFormat.OBJECT,
notificationMode: NotificationMode.ALL,
});
styleObserver.observe($element);
Here is a live demo that is using this technique:
See the Pen CSS Anchor Positioning: Animating `position-area` with View Transitions, powered by @bramus/style-observer by Bramus (@bramus) on CodePen.
Scroll the page up and down to trigger a different position-area on the positioned element.
~
Known Issues
Unfortunately there are some issues with the technique:
-
The code does not work in Firefox.
I initially thought this was because of
position-areabeing underspecified in the spec – it’s animation type is currently specced as “TBD” – and that Firefox therefore did not mark the property as being Discretely Animatable. A look at Stylo’s source code tells me it is defined as a discretely animatable property so so that’s not the problem.Digging into the code of my
StyleObserver, I see it only picks up the initially applied value ofposition-areabut no subsequent changes, even though agetComputedStyle()indicates that the value did change. I think there is a bug on Firefox’s end in which it does not trigger a transition when the value changes as the result of aposition-try-fallbackbeing chosen.I have filed w3c/csswg-drafts#13577 at the CSSWG to fix the spec, and https://bugzilla.mozilla.org/show_bug.cgi?id=2020592 with Firefox to get the bug sorted on their end.
-
Chrome is affected by a 1-frame glitch due to the ambiguously defined timing of
transitionrun. w3c/csswg-drafts#11665 is concerned with this. -
In Safari, the
transitionrunthat tracksposition-areakeeps firing over and over once it has detected a change. This is fixed in Safari Technology Preview. -
While the View Transition is running, the positioned and anchored elements can feel a bit out-of-sync. This is because of how View Transitions deal with scroll: during a scroll, VTs retarget the end
transformof the::view-transition-group()pseudos, which makes them be subjected to theanimation-durationinstead of changing instantly. In w3c/csswg-drafts#10197 I am throwing around ideas to get this fixed.
All these issues are fixable over time, but I would say that the 1-frame glitch in Chrome is preventing this from being something that is really usable right now.
Also note that the anchor in the demo does not change aspect ratio when a new position-area gets set. If yours does, you’ll need this code by Jake.
~
🔥 Like what you see? Want to stay in the loop? Here's how:
I can also be found on 𝕏 Twitter and 🐘 Mastodon but only post there sporadically.
This content originally appeared on Bram.us and was authored by Bramus!
Bramus! | Sciencx (2026-03-02T22:19:20+00:00) Experiment: Animating CSS position-area with View Transitions. Retrieved from https://www.scien.cx/2026/03/02/experiment-animating-css-position-area-with-view-transitions/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.