New Mar 2, 2026

Experiment: Animating CSS position-area with View Transitions

More Front-end Bloggers All from Bram.us View Experiment: Animating CSS position-area with View Transitions on bram.us

Recording of the demo.

~

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:

  1. The code does not work in Firefox.

    I initially thought this was because of position-area being 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 of position-area but no subsequent changes, even though a getComputedStyle() 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 a position-try-fallback being 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.

  2. Chrome is affected by a 1-frame glitch due to the ambiguously defined timing of transitionrun. w3c/csswg-drafts#11665 is concerned with this.

  3. In Safari, the transitionrun that tracks position-area keeps firing over and over once it has detected a change. This is fixed in Safari Technology Preview.

  4. 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 transform of the ::view-transition-group() pseudos, which makes them be subjected to the animation-duration instead 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.

Scroll to top