I had this (bad) idea.
It’s related to popovers and anchor-positioned menus. I love this pairing: with only HTML and CSS, we can make a button that opens/closes anything we want. A tooltip or a menu is a wonderful use-case.
This isn’t a terribly difficult thing to do, but you have to remember a bunch of stuff and put certain unique values on certain elements exactly.
1. Remember the right `command` attribute value on the button
2. Put a unique `id` on the menu.
3. Match up the `commandfor` attribute on the button to that id.
4. Make sure the button has a unique `anchor-name`.
5. Match up the `position-anchor` on the menu to that unique name.
6. Make sure you’re using good anchor positioning fallbacks.
That feels like kind of a lot to remember and get right.
Here’s my (bad) idea: make a quick “ that does those things. On the surface, maybe that makes sense. It did to me. But the ridiculous part is that now it introduces JavaScript into things in a place we didn’t need JavaScript before, which makes it more fragile (and potentially render later) than it would without.
So I’m not advocating for use here, but I did learn some things along the way that I found interesting and worth sharing.
## Light DOM Web Component
I called it “ just to be short and slightly cheeky.
“`javascript
import { LitElement, html, css } from ‘lit’;
import { customElement, property } from ‘lit/decorators.js’;
@customElement(‘a-menu’)
export class AMenu extends LitElement {
@property({ attribute: ‘button-name’ }) buttonName = ‘Menu’;
private menuId = `menu-${Math.random().toString(36).substr(2, 9)}`;
createRenderRoot() {
return this;
}
firstUpdated() {
const menu = this.querySelector(‘menu’);
if (menu) {
menu.setAttribute(‘popover’, ‘auto’);
menu.id = this.menuId;
}
}
render() {
return html`
a-menu {
display: inline-block;
button {
position-anchor: –menu-button-${this.menuId};
}
menu {
position-anchor: –menu-button-${this.menuId};
position-area: block-end span-inline-start;
position-try: flip-block, flip-inline, flip-block flip-inline;
inset: unset;
margin: 0;
}
}
`;
}
}
“`
Then usage is as simple as this:
Notice we **don’t** need to:
1. Remember a unique ID on the menu.
2. Remember the popover commands.
3. Remember to attach an `anchor-name` or `position-anchor` to put the menu next to the button.
### … but now we have a problem.
Even though we’re putting a unique ID on the menu and using unique custom idents on the anchors, the **first** menu will open in the position of the **last** button. Why? Because we’re using the Light DOM here, and the last generic `a-menu menu {}` selector will **override** the first one, making **all** buttons/menus use the values of the last one.
## Using @scope
It occurred to me that a potential fix here is the newfangled `@scope` in CSS. If we updated the style block to be this instead:
“`javascript
@scope {
:scope {
display: inline-block;
button {
anchor-name: –menu-button-${this.menuId};
}
menu {
position-anchor: –menu-button-${this.menuId};
position-area: block-end span-inline-start;
position-try: flip-block, flip-inline, flip-block flip-inline;
inset: unset;
margin: 0;
border: 0;
