fast
fast copied to clipboard
rfc: replace `anchored region` with utility from `floating-ui`
💬 RFC
This issue proposes the deprecation of Anchored Region
in favor of using utility provided via floating-ui. Floating UI provides two key features, but for our scenarios we only require the primary functionality - "Anchored positioning primitives". Floating UI describes this in the following way:
CSS is currently missing a feature called “anchored positioning” — the ability to anchor an element (like a tooltip) to another one (like a button) while simultaneously keeping it in view as best as possible by avoiding clipping and overflow.
Attempting to do fully dynamic anchored positioning with today’s plain CSS is not possible. Floating UI provides a JavaScript implementation of this feature.
FAST is well aware of this problem which has to this point been solved using Anchored Region
, an HTML element with utility for positioning elements relative to an anchor and within a certain viewport.
🔦 Why move to Floating UI
?
The first thing I'll note here is that in terms of timeline, we're in the middle of making larger scale breaking changes, which makes "now" the right time to investigate a significant API change and deprecation.
A larger reason for this shift is that of future platform alignment. While the concept of "anchored positioning" is missing on the web platform, work is currently under way to bring this behavior to the web platform and the longer we wait to remove obstacles, the more out of alignment we will continue to get. An initial "explainer" document was published by the Microsoft Edge team just over a year ago. That work has likewise been moved into an unofficial draft spec for the CSS Working Group which was recently discussed with a note that it may soon move into the CSS WG repository with a coming ask for "Editor Draft" status. Beyond this, the Chrome team has been actively working to implement/prototype this behavior, you can see current status at this tracking issue.
Currently, our implementation for anchored positioning takes a very prescriptive approach centered around a custom element, Anchored Region, which requires both DOM and element registration. That is a significant hurdle to clear in terms of future platform alignment. Removing that barrier now is a significant step forward and in alignment with pursuing a forward-thinking path of alignment with the web platform.
An additional benefit here is that by removing the additional DOM from our core primitives, we make them easier for folks to both implement and extend on their own, which is a key value of FAST as a project.
Floating UI is a javascript library, which allows us to remove the DOM and custom element implementation in favor of a relatively lightweight utility for anchored positioning. As Anchored Positioning comes online, we can maintain our JS implementation when the CSS property is not supported. This sets us up for a backwards compatible API for browsers which haven't yet implemented, while allowing us to take advantage of the latest/greatest of the web as it comes online.
💻 Examples
Below is an example of menu implemented with nested menus using Floating UI for anchored positioning. To see an example of how this might work, you can checkout the following branch users/chhol/use-floating-ui-for-anchored-positioning
.
https://user-images.githubusercontent.com/13071055/177406496-9df9d859-5514-4019-889f-e4096a628de3.mov
Do the positioning "options" closely match the ones in the proposed css spec? ie. does it really avoid a future api change. Can it do nested floaters? ie. menu items with menu items We probably lose the menu "fill" behavior, but I think only picker
uses that.
Do the positioning "options" closely match the ones in the proposed css spec? ie. does it really avoid a future api change. Can it do nested floaters? ie. menu items with menu items We probably lose the menu "fill" behavior, but I think only
picker
uses that.
I've got nested menu items in the above video example - I think it would work going deeper as well as far as I can tell. In terms of positioning, I don't know if the current draft would be 100% 1:1 with the values that floating-ui
has, but what is proposed is very, very close: https://tabatkins.github.io/specs/css-anchor-position/#anchor-pos. At worst, we'd only be updating/changing the values allowed for positions (either adding, removing, or modifying), which is a much smaller break. If floating-ui
supported more values than the CSS implementation, we could always continue to use the package for those while offloading the supported values to CSS.
TLDR; I don't know that we can avoid any breaking changes here, but we can get into a position that makes it much easier for us to leverage this by removing larger deltas such as DOM and registration as requirements.
I did some experimentation on my own with the floating-ui
library recently and found it very easy to use. In particular, I needed to implement context menus that were positioned relative to where the mouse had been clicked on a particular element. This was very easy to do by combining fluent-ui
with our menu
component. It wasn't as clear how to accomplish the exact same thing with our anchored-region
.
Since the library is pretty small, and would likely cover not only our component scenarios but other application scenarios like above, while aligning better with upcoming standards, I'd be in favor of this shift and do agree that if we're going to move this direction, now is probably the best time to do it. Otherwise, we need to wait another year or more and probably still have to build-in a migration/deprecation path.
+1, before anchored region was released I actually implemented this in my own design system using @popperjs/core
instead of floating-ui
which looks just as capable. Taking third party dependencies is hard sometimes but there is a lot of nuance to anchored popups which I didn't want the maintenance overhead from when there is much prior art.
Edit: Looks like floating-ui
is essentially just popper v3
Even though we mostly rely on fast foundation now in our codebase, we have done the same with floating UI.
Also, aligned as much as possible, to the popup explainer with its interface
https://github.com/Vonage/vivid-3/tree/main/libs/components/src/lib/popup
I think this is a good idea. My team has already started using floating-ui with our fast powered components due to how simple it was to get it to do what we needed. Solved some problems we were having with positioning (particularly with the dropdown) very quickly and easily with floating-ui.
While floating-ui
is very easy to use and works wonders, one adavantage that AnchoredRegion
currently has over it is the concept of inset positioning. AnchoredRegion
has the ability to position floating elements along the inner edge of a reference element while floating-ui cannot, at least not in a way that is documented or without writing a custom middleware.
This could be a moot point since inset positioning can be handled by css(usually) but it is something to note when feature parity is concerned.
how about moving forward to native popup? https://developer.chrome.com/blog/pop-ups-theyre-making-a-resurgence/
there's a polyfill to be used until safely supported https://github.com/oddbird/popup-polyfill
how about moving forward to native popup? https://developer.chrome.com/blog/pop-ups-theyre-making-a-resurgence/
there's a polyfill to be used until safely supported https://github.com/oddbird/popup-polyfill
This is the long term goal; I think I'd like to see the polfyfill tested using light DOM and shadow DOM implementations just to ensure that custom elements are fully supported. If you're open to exploring or creating a couple of prototypes (even just using stackblitz) which illustrate that the polyfill works well with a few shadow DOM implementations (such as nested shadow DOM within a template perhaps) that would help accelerate a conversations around converging on the platform APIs.
I had a very quick look at the polyfill implementation this morning. It's pretty straight forward. I think the main thing is that it doesn't handle all the same issues that Floating UI does, because it doesn't handle positioning in any sort of way (makes sense...that is part of the CSS anchor work). So, if we went with this polyfill (which basically just hides/shows things and makes them position fixed) then we still need a solution to the other pieces.
I had a very quick look at the polyfill implementation this morning. It's pretty straight forward. I think the main thing is that it doesn't handle all the same issues that Floating UI does, because it doesn't handle positioning in any sort of way (makes sense...that is part of the CSS anchor work). So, if we went with this polyfill (which basically just hides/shows things and makes them position fixed) then we still need a solution to the other pieces.
Yeah, I think to move to the platform solution we need both the Popup and the CSS Anchored Positioning. We could certainly use Popup for our surfaces that need to sit on the top layer, but we'd definitely need a solution to handle positioning as well.
I did some experimentation on my own with the
floating-ui
library recently and found it very easy to use. In particular, I needed to implement context menus that were positioned relative to where the mouse had been clicked on a particular element. This was very easy to do by combiningfluent-ui
with ourmenu
component. It wasn't as clear how to accomplish the exact same thing with ouranchored-region
.
Anchoring to a coordinate isn't currently supported by anchored region. Not sure how difficult it would be to add. Note that anchored region perf is significantly better than floating ui for things like adjusting placement during screen resizes, for example.
Draft PR which adds support for anchoring to an arbitrary coordinate and mouse tracking to anchored region. Turned out to be quite straightforward to add. Mouse tracking is a scenario where anchored-region perf advantage would be noticable IMO. https://github.com/microsoft/fast/pull/6389
One thing I'd be interested in is, if we stay with anchored region, could we break it into two pieces. One would be a pure imperative JS API that could be called to anchor/position things, and then another would be the web component built on top of that API. One of the things that was really nice about Floating UI is that JS API. When I played around with it, it made it really easy to do dynamically spawned context menus, for example.
One thing I'd be interested in is, if we stay with anchored region, could we break it into two pieces. One would be a pure imperative JS API that could be called to anchor/position things, and then another would be the web component built on top of that API. One of the things that was really nice about Floating UI is that JS API. When I played around with it, it made it really easy to do dynamically spawned context menus, for example.
I've been wondering something similar. An imperative API with the component as well as directives built on top of it would provide a lot of flexibility. https://github.com/microsoft/fast/pull/6345#issuecomment-1242646380
Probably. One "wrinkle" is this - one of the perf optmizations of anchored region is that it does not get into the business of examing the margins/padding that may be applied to the element it is positioning (ie. no wasting cpu cycles looking at css styles), it just places the element directly next to the anchor based on client rects. So if you want your popup to have a margin between it and the anchor element you probably end up with a wrapper html element anyway. Not insurmountable but may complexify the api a bit.
Would it make sense to keep that behavior as the default, but also provide the more expensive "convenience" features as opt in like how repeat
handles its positioning options?
Would it make sense to keep that behavior as the default, but also provide the more expensive "convenience" features as opt in like how
repeat
handles its positioning options?
It could just be making it clear in the docs that using css styles like "margin" to further adjust the placement of a positioned element will likely fail. Maybe adding some anchor-marginX/Y props for people to use for adjustments.
@chrisdholt Can this be closed as done now?
@chrisdholt Can this be closed as done now?
Tooltip is still outstanding on this, so while most existing things have transitioned, it's not quite "done" fully yet.
Able to be closed now???