css-anchor-positioning
css-anchor-positioning copied to clipboard
[BUG] Polyfill function fails with an error in Vite/Vue 3 app
Great work on the polyfill! Haven't gotten it working yet, but hopefully I'll manage to.
Describe the bug It is impossible for me to apply the polyfill. Getting the same error (different stack trace depending on the method used) when applying via Vite in dev mode, Vite bundle, or loading from unpkg (not my preference, as I don't want to add that to the CSP for production).
Example stacktrace:
Uncaught (in promise) TypeError: e.children is undefined
ir utils.ts:70
Tm parse.ts:306
h create.js:169
Xr create.js:108
h create.js:180
k create.js:195
reduce List.js:174
Xr create.js:103
h create.js:180
l create.js:239
Tm parse.ts:304
B @oddbird_css-anchor-positioning_fn.js:34
B @oddbird_css-anchor-positioning_fn.js:20
Tm parse.ts:294
qm polyfill.ts:460
o @oddbird_css-anchor-positioning_fn.js:23
promise callback*c @oddbird_css-anchor-positioning_fn.js:33
o @oddbird_css-anchor-positioning_fn.js:23
promise callback*c @oddbird_css-anchor-positioning_fn.js:33
o @oddbird_css-anchor-positioning_fn.js:23
promise callback*c @oddbird_css-anchor-positioning_fn.js:33
B @oddbird_css-anchor-positioning_fn.js:34
B @oddbird_css-anchor-positioning_fn.js:20
qm polyfill.ts:446
setTimeout handler* application.ts:42
onReady shim.ts:13
async* application.ts:34
This happens no matter if I load the Polyfill before or after initializing the rest of the app, no matter if any CSS rules actually specify any anchor properties.
The line numbers seem to be a little off (possibly a sourcemap issue?). I assume the line in question is line 72.
FYI the example page is working for me.
To Reproduce It's a fairly complex app; I'll look into creating an example repo in the coming days, but I wanted to see if, maybe, from the stack trace, this is already something you can spot.
Code I'm using:
import polyfill from '@oddbird/css-anchor-positioning/fn'
if (!("anchorName" in document.documentElement.style)) {
if (document.readyState !== 'complete') window.addEventListener('load', () => { polyfill() })
else polyfill()
}
Example I'm trying to get working:
<template>
<li>
<Avatar :userId class="avatar focusable" :popovertarget="id" />
<div :id class="dropdown" popover="auto">This is the dropdown!</div>
</li>
</template>
<style scoped lang="scss">
.avatar {
anchor-name: v-bind(anchorName);
}
.dropdown {
padding: 10px;
left: unset;
bottom: unset;
top: anchor(v-bind(anchorName) bottom);
right: anchor(v-bind(anchorName) right);
}
</style>
<script setup lang="ts">
import { userIdRef } from '@/pinto'
const userId = computed(() => userIdRef.value)
const id = useId()
const anchorName = useAnchorName(id)
</script>
Expected behavior Polyfill to be applied.
Screenshots
Firefox:
Safari:
Browsers tested on Firefox 130, Safari 18.1
It is, unfortunately, more complicated than I thought. I was unable to reproduce it in a simple Vite app.
Debugging further:
Vite with vite-ruby generates a link tag in the main template that looks like this:
<link rel="stylesheet" href="/vite-dev/entrypoints/application.scss" data-turbo-track="reload" />
However, the content served by vite-dev is neither CSS nor SCSS, but JavaScript instead (for HMR), and looks somewhat like this:
import { createHotContext as __vite__createHotContext } from "/vite-dev/@vite/client";import.meta.hot = __vite__createHotContext("/entrypoints/application.scss");import { updateStyle as __vite__updateStyle, removeStyle as __vite__removeStyle } from "/vite-dev/@vite/client"
const __vite__id = "/Users/konstantin/Workspace/good-beans/pinto/app/frontend/entrypoints/application.scss"
const __vite__css = "... ACTUAL CSS HERE ..."
__vite__updateStyle(__vite__id, __vite__css)
import.meta.hot.accept()
import.meta.hot.prune(() => __vite__removeStyle(__vite__id))
I assume this then gets added somewhere else so just a guard might help? Seeing if I can work it out.
Update:
Just guarding against the lack of children (i.e. if (!rule || !rule.children) return []; instead of if (!rule) return [];) does not work, though it does obviously make the error go away.
I have not managed to reproduce this without vite-ruby, but I did manage to do so with it and a basic Rails app, no Vue needed at all.
Example repo: https://github.com/rkh/vite-rails-example
Reproduce:
- Prerequisites: Recent Ruby, recent Node.js
- Run
bundle install && npm installto install dependencies - Run
bin/vite devto start the Vite dev server, runbin rails/serverto run the Rails server - Open http://localhost:3000/ in Firefox
@rkh Thanks for the repo!
Is application.scss loading correctly for you? In the demo app, I am getting a 404, which means that the polyfill is trying to parse the 404 content, which is HTML. (I had to make some tweaks to get it running in my local Ruby environment, and I may have messed something up). However, this gives the same outcome as if it were JS, in that the parser can't parse the content, and it ends up erroring.
I think one task is for the polyfill to better handle <link>s that don't actually contain CSS.
Also, I would expect the polyfill to not work well with HMR- it currently only is applied once- see #91.
Does the polyfill still work as expected in the built app?
Yes, sorry, also getting the 404, only checked that it was the same error trace.
I only tested it on non-dynamic elements, so I assume it won't work in the built app, either (can confirm that for you if you want).
Feel free to close this issue if it is out of scope for you. I ended up not using the polyfill at all.
Off topic: What I ended up doing.
Unfortunately, relative positioning of popovers behaves differently between Firefox/Safari on the one hand and Chrome on the other, and thus I needed two different ways to implement this:
$avatar-size: 32px;
$offset-x: 0px;
$offset-y: 2px;
@supports (anchor-name: --example) {
.avatar {
anchor-name: v-bind(anchorName);
}
.dropdown {
left: unset;
bottom: unset;
top: calc(anchor(v-bind(anchorName) bottom) + size($offset-y));
right: calc(anchor(v-bind(anchorName) right) + size($offset-x));
}
}
@supports not (anchor-name: --example) {
.dropdown {
position: relative;
align-self: start;
left: unset;
bottom: unset;
top: unset;
right: unset;
transform: translate(calc(size($avatar-size + $offset-x) - 100%), size($avatar-size + $offset-y));
}
}
@rkh I would be interested in hearing if the polyfill works in a built vite rails app. I would expect it to work if the css file is served as CSS- confirmation would help us narrow down where this is an issue. Thanks!
The 404 issue can be reproduced in the demo app by adding <link rel="stylesheet" href="/fake.css" /> to the header.
@rkh I would be interested in hearing if the polyfill works in a built vite rails app. I would expect it to work if the css file is served as CSS- confirmation would help us narrow down where this is an issue. Thanks!
I can confirm the following for @oddbird/css-anchor-positioning 0.2.0 on Rails 7.1.4 with Vite 5.4.7:
- The error does not happen when running a normal build (ie development without HMR, or production mode)
- The polyfill is not applied to dynamically added Vue components, but works otherwise
Keep in mind that using Vite and Vue is not the standard Rails way. Instead the recommended approach is using Hotwire. This can result in HTML elements sometimes being dynamically added, and sometimes being included in the original HTML/CSS, so I'd suspect the polyfill would be unreliable in that scenario.
This is released in v0.3.0.