Ready-made recipes for dropdown menus and tooltips that never spill past the edge
Beyond the built-in shorthands like flip-block, @position-try lets you define your own named fallback positions, complete with their own viewport safety margin. This article walks through ready-to-copy recipes for dropdown menus, nested submenus and tooltips that reliably stay inside the visible area.
Table of Contents
- 1. Why menus have different requirements than plain tooltips
- 2. The syntax of @position-try: which properties are allowed
- 3. Building in a safety margin from the viewport edge
- 4. Recipe: a dropdown menu that never spills past the edge
- 5. Recipe: a tooltip whose width is tied to its anchor
- 6. Nested submenus with their own anchor chain
- 7. Anchor Positioning inside scrollable containers
- 8. Common pitfalls with custom position-try recipes
- 9. Checklist before shipping a menu or tooltip recipe
- 10. Summary
- 11. FAQ
1. Why menus have different requirements than plain tooltips
A dropdown menu must never spill past the screen edge, because clipped menu items are not just ugly, they simply become unusable. Unlike a plain info tooltip that can get away with sitting somewhat awkwardly, a menu has to keep every single one of its entries fully clickable, which raises the bar for the fallback logic considerably.
The built-in shorthands flip-block and flip-inline cover many tooltip cases just fine, but hit their limits with menus once you need extra spacing from the viewport edge, a minimum width, or an alignment tied to the anchor's own width. That is exactly what the @position-try rule exists for: it lets you define a complete, named position with all the extra properties you need once, and reuse it across as many components as you like.
2. The syntax of @position-try: which properties are allowed
A @position-try rule is declared with a name of your own choosing in the --dashed-ident format and contains, inside its curly braces, a subset of positioning properties: position-area or the individual inset values, margin, width and height, as well as self-alignment properties like justify-self. Non-layout properties such as colors or fonts are deliberately not allowed here, because the rule is meant exclusively for positioning.
This restriction keeps a @position-try rule clearly separated from the component's visual styling. The menu itself gets its background color, shadow and border through the regular class rule as usual, while the @position-try rule is responsible solely for the spatial alternative. This separation makes it easy to reuse the same visual component in different places on the page with different fallback positions.
3. Building in a safety margin from the viewport edge
A menu that touches the screen edge directly looks cramped and is harder to hit on touch devices. Inside a @position-try rule this margin can be set with margin or explicit inset values, combined with calc() to guarantee a fixed minimum distance regardless of the window size.
For especially robust menus, a fixed max-width combined with width: max-content also helps, so long menu items do not cause the menu to grow past the safety margin defined by the @position-try rule. That way, even the least favorable fallback case is still guaranteed to fit within the visible area.
@position-try --menu-safe-bottom {
position-area: bottom span-right;
margin-block-start: 0.5rem;
margin-inline-end: max(1rem, env(safe-area-inset-right));
max-width: min(20rem, calc(100vw - 2rem));
}
4. Recipe: a dropdown menu that never spills past the edge
The following recipe combines a trigger button with a menu opened through popover. Two named @position-try rules cover the cases where the button sits in the right or bottom half of the screen, each using the same safety margin introduced in the previous section.
The order inside position-try-fallbacks matters: first the variant that flips horizontally, then the one that additionally flips vertically, so the menu still finds a sensible position even in the least favorable screen corner, instead of simply falling back to whatever alternative was checked last.
<button popovertarget="user-menu" style="anchor-name: --user-trigger;">
Account
</button>
<ul id="user-menu" popover class="menu">
<li><a href="/profile">Profile</a></li>
<li><a href="/settings">Settings</a></li>
<li><a href="/logout">Log out</a></li>
</ul>
<style>
@position-try --menu-flip-left {
position-area: bottom span-left;
margin-block-start: 0.5rem;
}
@position-try --menu-flip-top {
position-area: top span-left;
margin-block-end: 0.5rem;
}
.menu {
position: absolute;
position-anchor: --user-trigger;
position-area: bottom span-right;
margin-block-start: 0.5rem;
min-width: anchor-size(width);
position-try-fallbacks: --menu-flip-left, --menu-flip-top;
}
</style>
5. Recipe: a tooltip whose width is tied to its anchor
A tooltip often should not grow wider than the element that triggered it, so it visually stays calm inside a form or a table row. The anchor-size() function reads the anchor's width or height and can be plugged directly into the tooltip's width, min-width or max-width, regardless of which fallback position is currently active.
Combined with a @position-try rule that additionally sets a fixed max-width as an upper bound, the tooltip stays readable even when the anchor itself is very wide, for example an entire table row. This combination of anchor-size() and an explicit ceiling stops the tooltip from tipping into either extreme, too narrow for its text or uncomfortably wide for a single word.
@position-try --tooltip-capped {
position-area: top;
margin-block-end: 0.4rem;
max-width: 22rem;
}
.field-tooltip {
position: absolute;
position-anchor: --field-trigger;
position-area: top;
min-width: anchor-size(width);
max-width: min(anchor-size(width, 1.5), 22rem);
position-try-fallbacks: --tooltip-capped, flip-block;
}
6. Nested submenus with their own anchor chain
A submenu that opens on hover or click of a menu item needs its own anchor-name on that specific menu item and its own, independent position-try-fallbacks chain. It matters that every menu item capable of opening a submenu gets a unique anchor name, ideally generated dynamically, so that several simultaneously open submenus do not overwrite each other.
For a submenu's fallback chain, right works well as the preferred default position, with flip-inline as the first fallback, so a submenu that would open at the right screen edge automatically switches to the left instead. This logic works independently of the parent menu's own position, because every anchor relationship is evaluated on its own.
7. Anchor Positioning inside scrollable containers
When the anchor does not sit directly in the viewport but inside a scrollable container like a table with a fixed height, the fallback logic by default relates to the nearest matching containing block, not necessarily the entire viewport. In most cases that is exactly the desired behavior, because a tooltip inside a scrollable table should not spill past its edge, even if there is still room elsewhere in the viewport.
If instead you explicitly want an element to orient itself against the whole viewport rather than the surrounding scroll container, the position: fixed property combined with Anchor Positioning helps, because it explicitly raises the containing block to the initial containing block. This distinction should be made deliberately for every component, rather than relying on whatever the default behavior happens to be.
8. Common pitfalls with custom position-try recipes
A frequent mistake is assigning the same anchor-name twice: if two different elements point to the same name, the positioned element binds to whichever one appears last in the document. That leads to seemingly random misbehavior that is hard to trace unless you specifically go looking for duplicate names.
A second stumbling block is setting position-try-fallbacks on an element that itself references no position-anchor and is not positioned absolutely or fixed. Without that prerequisite the browser silently ignores the entire fallback chain, without printing any error, which makes debugging unnecessarily hard in practice.
9. Checklist before shipping a menu or tooltip recipe
Before going to production, it pays off to give every component a short but systematic test against all four screen edges, with both long and short text content, and inside a narrow mobile viewport. Once you write these combinations down as a checklist, you can reuse it for every new component instead of rediscovering all the edge cases from scratch on every project.
Equally important is a check of actual keyboard and screen reader operation, because the spatial fallback position is purely visual and changes nothing about the focus order. A menu that is positioned perfectly on screen but unreachable via keyboard has still missed its actual goal.
| Recipe building block | CSS property | Purpose |
|---|---|---|
| Safety margin from the edge | margin with calc() inside @position-try |
Menu never touches the viewport edge directly |
| Tying width to the anchor | anchor-size(width) in min-width/max-width |
Tooltip stays neither narrower nor much wider than its trigger |
| Unique submenu anchors | Dynamically generated anchor-name per menu item |
Several simultaneously open submenus do not overwrite each other |
| Viewport instead of scroll container | position: fixed instead of position: absolute |
Positioning relates to the entire viewport |
| Checking the fallback prerequisite | position-anchor plus position: absolute/fixed |
Prevents silently ignored fallback chains |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
Position-Try Recipes for Menus and Tooltips: The Essentials at a Glance
Custom fallback rules
@position-try defines named, reusable positions with their own margin, width and alignment.
Menu recipe
Two @position-try rules with horizontal and vertical flip robustly cover every screen corner.
Tooltip recipe
anchor-size() ties the tooltip's width to its anchor, a max-width prevents uncomfortably wide tooltips.
Common mistakes
Duplicate anchor-name values and a missing position-anchor lead to silently ignored fallback chains.