Een onthuld actievenster of een pop-over-knopgroep is een handig patroon waarmee gebruikers toegang krijgen tot extra functies terwijl er minimale ruimte wordt ingenomen. Dit overlaypatroon heeft een complexe vormgeving, omdat het deelvenster aan een triggerelement gekoppeld moet blijven terwijl het zich aanpast aan de beperkingen van de viewport. Traditioneel waren hiervoor complexe JavaScript-bibliotheken (zoals Popper.js of Floating UI) nodig om posities te berekenen en botsingen af te handelen.
CSS-ankerpositionering biedt een declaratieve, prestatiegeoptimaliseerde manier om deze relaties volledig in CSS te verwerken, zodat browsers de positionerings- en overlooplogica native kunnen beheren.
1. De relatie tussen de knop en het deelvenster definiëren
De eerste stap is een triggerknop maken die de overlaycontainer opent via de Popover API.
VERPLICHT onderscheid voor toegankelijkheid: Dit patroon modelleert expliciet een knopgroep die wordt getoond in een popover in plaats van een echt ARIA-menu. Pas role="menu" of role="menuitem" alleen toe als je het bijbehorende contract voor toetsenbordnavigatie volledig implementeert (zoals de verwerking van ruimtelijke navigatie met de pijltoetsen tussen items). Voeg om dezelfde reden aria-haspopup niet toe aan de trigger. De waarde (menu, listbox, tree, grid of de verouderde true) is een belofte dat het doel een overeenkomende rol toont, wat dit patroon niet doet.
<button popovertarget="action-panel">
Open Actions
</button>
<!-- Use the Popover API (`popover="auto"`) for the overlay to ensure it is placed in the top layer and handled accessibly by the browser. -->
<div id="action-panel" popover="auto" class="panel">
<button class="action-item" type="button">Edit</button>
</div>
Hiermee wordt een impliciete ankerkoppeling gemaakt tussen de knop en het deelvenster, zodat het deelvenster relatief ten opzichte van de knop kan worden geplaatst zonder dat er anchor-name op de knop of position-anchor op het deelvenster nodig is.
2. Positionering met position-area
Gebruik in plaats van handmatige top/left-verschuivingen position-area om het doel op een 3x3-raster te plaatsen ten opzichte van het anker.
.panel {
/*
Position the panel below the anchor (block-end),
aligned to the start of the anchor and spanning to its end (span-inline-end).
*/
position-area: block-end span-inline-end;
/* Reset insets to allow the grid to take control */
inset: auto;
}
Geef de voorkeur aan logische zoekwoorden (span-inline-end, block-start) boven fysieke zoekwoorden (left, top) om RTL en verschillende schrijfmodi automatisch te ondersteunen.
VERPLICHT: Meng geen fysieke en logische zoekwoorden in position-area.
3. Edge-veerkracht (back-ups) implementeren
Definieer 'probeer tactieken' die de browser moet proberen als de standaardpositie overloopt om te voorkomen dat het deelvenster aan de rand van het scherm wordt afgekapt.
.panel {
/*
If the panel overflows the bottom, flip it to the top (flip-block).
If it overflows the inline edges, flip it horizontally (flip-inline).
If it overflows both (e.g., in a corner), flip on both axes (flip-block flip-inline).
*/
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}
Als je dropdownmenu of flyout een richtingspijl of rand bevat waarvan de stijl moet veranderen, afhankelijk van de gekozen reservepositie, ga je naar Positiebewuste tooltips. Ga naar Declaratieve dialoogvenster- en pop-overbeheer voor declaratieve aanroeping van dialoogvensters en pop-overs zonder JavaScript.
Reservestrategieën
Fallbacks en browserondersteuning voor Popover
De Popover API is voornamelijk progressive enhancement, maar de bepalende gedragingen (promotie van de bovenste laag, licht sluiten en aanroepen van popovertarget) hebben geen equivalent dat alleen CSS gebruikt. Oudere browsers hebben een polyfill nodig, of een handmatige fallback als je er liever geen verzendt.
Polyfill: Als je het kenmerk popover wilt ondersteunen in oudere browsers, laad je @oddbird/popover-polyfill voorwaardelijk. VERPLICHT: Voer een functiedetectie uit door te controleren op de property popover op HTMLElement.prototype en laad de polyfill alleen als systeemeigen ondersteuning ontbreekt. Laad de polyfill NIET onvoorwaardelijk.
Met een bundler of importtoewijzing:
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
Zonder bundler importeer je vanuit een CDN in een <script type="module">:
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
Opmerking over de stijl: De polyfill kan de echte pseudoklasse :popover-open niet definiëren, dus wordt in plaats daarvan de klasse .\:popover-open toegepast. VERPLICHT: Combineer de 2 met :is() of :where(), anders negeren browsers zonder :popover-open de hele regel:
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
Als alternatief kun je voor een verouderde fallback zonder polyfill position: fixed gebruiken en de coördinaten handmatig berekenen via getBoundingClientRect() of vertrouwen op de standaardpositionering met inset: auto als dat acceptabel is voor de use case.
Als je browsers zonder ankerpositionering wilt ondersteunen, moet je een redelijke positie instellen. Standaard worden pop-overs gecentreerd in het midden van het scherm, wat misschien werkt voor jouw use case.
Voor sommige use cases kun je de @oddbird/css-anchor-positioning-polyfill gebruiken, die ondersteuning toevoegt voor bepaalde use cases voor ankerpositionering. De functie ondersteunt geen impliciete ankers, dus je MOET ankernamen toevoegen aan de trigger. Daarnaast wordt position-area niet ondersteund voor pop-overs door de polyfill, dus je MOET anchor() gebruiken voor de gewenste insets.
<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
if (!("anchorName" in document.documentElement.style)) {
await import("https://unpkg.com/@oddbird/css-anchor-positioning");
}
</script>
.panel {
/* Mandatory: use explicit anchor name */
position-anchor: --kebab-anchor;
/* Mandatory: use insets rather than position-area for positioning */
bottom: auto;
right: auto;
top: anchor(bottom);
left: anchor(left);
margin: 0;
}