The new way of positioning in CSS
The new way of positioning in CSS

The new way of positioning in CSS

Raise your hand if you've ever spent three hours writing a JavaScript script just to keep a damn tooltip from going off-screen.

Yeah, you too. Don't lie.

For decades, positioning in CSS was like giving orders to an over-eager and not-so-bright intern.

Us: "Put yourself at the bottom right of the button."

CSS (in position: absolute): "OKAY, BOSS!"

Us: "But... you're off the screen!"

CSS: "I DID WHAT YOU SAID. BOTTOM RIGHT. I AM AT THE BOTTOM RIGHT. THAT'S YOUR PROBLEM NOW."

The result? The user had to deal with the dreaded "horizontal scrollbar of shame" to read the end of a menu.

To fix this, we brought out the heavy artillery: JavaScript. We invoked the hellish mantra getBoundingClientRect(), calculated the viewport width, did math (IN OUR HEADS!) to see if there was space left, and added a please-menu-go-left class.

It was ugly. It was heavy. It was fragile.

Well, my fellow traumatized colleagues, I have news. CSS stopped skipping class. It finally understands how space works. And it just fired our position-helper.js script.

Here is CSS's 3-step plan to (finally) manage pop-ups intelligently.

Step 1: The Native ON/OFF Button (The Popover API)

The first thing our JS script did was manage the display. display: none here, display: block there. Not to mention the document.addEventListener('click', ...) to close the menu if you clicked elsewhere.

That's over.

HTML (yes, HTML!) is taking care of it.

Popover exempleindex.html
HTML
<!-- The button that turns on the TV -->
<button popovertarget="my-magic-menu">Open menu</button>

<!-- The TV -->
<div id="my-magic-menu" popover>
  I'm a menu!
</div>

That's it.

With just popover and popovertarget, the browser NATIVELY handles:

Your toggleMenu.js can go retire.

Step 2: The Leash (Anchor Positioning)

Okay, great, our menu appears. But it appears in the middle of nowhere. We want it to be attached to its button, like a little dog.

Before, we had to nest the menu inside the button (yuck) or use position: relative on a parent.

Now, we give them an invisible leash.

1. Put a collar on the button (the anchor):

Anchor button exemplestyle.css
CSS
.my-button {
  /* "Your name is 'anchor-button' now" */
  anchor-name: --anchor-button;
}

2. Attach the menu (the popover) to the leash:

Attach the menu (the popover) to the leashstyle.css
CSS
.my-menu {
  position: absolute; /* That part doesn't change */

  /* "Your reference is 'anchor-button'!" */
  position-anchor: --anchor-button;

  /* * Stick your TOP EDGE (top) 
   * on the BOTTOM EDGE (bottom) of the anchor!
   */
  top: anchor(bottom);
  left: anchor(left);
}

With anchor(), we can link any edge of our menu to any edge of our anchor. It's clean, it's declarative, and it works even if they aren't relatives in the DOM.

Step 3: The Plan B (Position Fallbacks)

This is where CSS starts to show off.

Our menu is securely attached. But we still have the same problem: if the button is all the way to the right, our menu will... yes, go off-screen. CSS is still a bit dumb.

...Except now, it has a brain.

We're going to give it positioning &quot;scenarios&quot; with a new rule: @position-try.

Position Try Exemplestyle.css
CSS
/* Scenario A (default):
   Try to open at the bottom right */
@position-try --bottom-right {
  top: anchor(bottom);
  left: anchor(left);
}

/* Scenario B (the fallback plan):
   If that doesn't work, try bottom left */
@position-try --bottom-left {
  top: anchor(bottom);
  right: anchor(right); /* <- The magic is here */
}

We've defined two plans. Now, we tell the menu to try them in order:

Try them in orderstyle.css
CSS
.my-menu {
  /* ... all the stuff from before ... */

  /* * "First try 'bottom-right'.
   * If it doesn't fit the screen, 
   * try 'bottom-left'."
   */
  position-try-fallbacks: --bottom-right, --bottom-left;
}

That's it. I'm serious. There is no other step.

The browser will now do ALL BY ITSELF:

  1. Attempt Plan A (--bottom-right).

  2. Realize (like a big boy) that &quot;Oops, that's going to overflow the viewport.&quot;

  3. Cancel Plan A.

  4. Attempt Plan B (--bottom-left).

  5. Realize that &quot;Ah, this one fits.&quot;

  6. Display the menu.

We can even add a Plan C (--top-left) and D (--top-right) to handle cases where the button is at the bottom of the page.

JavaScript Can Finally Take a Vacation

While CSS finally handles basic geography, what's our poor 200-line JS script going to do? It can go retrain. Make confetti on the screen, handle complex animations... fun stuff.

Positioning management is now the intern's job, and he (finally) passed his exams.

The only buzzkill: Like all good things, this is brand new. As I'm writing this, it's not yet available in all browsers. But it's arriving fast (thanks to Chrome, Firefox, and Safari for the ongoing work!).

Get ready: the era of getBoundingClientRect() is almost over. And frankly, no one will miss it.