Hovercards bieden een interactief voorbeeld van content als een gebruiker interesse toont in een link (door de cursor erop te plaatsen of de focus erop te leggen). Dit wordt aan gebruikers getoond voordat ze navigeren. Het biedt de gebruiker de content die deze nodig heeft of helpt te verifiëren dat de link de content bevat die de gebruiker zoekt.
Als je platte tekst en niet-interactieve tooltips nodig hebt in plaats van gestructureerde linkvoorbeelden, ga je naar Een tooltip tonen als je de muisaanwijzer op een element plaatst.
1. De aanwijskaart activeren
Gebruik de Interest Invokers API om de hovercard te activeren. Zo verschijnt de kaart zowel bij de muisaanwijzer als bij de focus en wordt de toegankelijkheid automatisch afgehandeld.
- Voeg het kenmerk
popover="hint"toe aan de kaartcontainer. Gebruikhintom te voorkomen dat niet-gerelateerde pop-overmenu's die misschien openstaan, worden gesloten. - Voeg het kenmerk
interestfortoe aan de triggerlink die naar deidvan de kaart verwijst.
<a href="/article" interestfor="article-preview">Article title</a>
<!-- The `id` must match the `interestfor` value on the trigger. -->
<div id="article-preview" popover="hint" class="hovercard">
<h2>The buzz on hummingbirds</h2>
<p class="byline">By Delphi Aguilar</p>
<div>
<img src="bird.jpg" alt="A vibrant hummingbird">
<p>Brief summary of the article...</p>
</div>
</div>
Je hoeft geen extra link naar de content in de hovercard op te nemen, omdat deze overbodig is voor de activerende link.
Ingebouwde toegankelijkheid in interestfor
Als je interestfor gebruikt, verwerkt de browser de bedrading van de ondersteunende technologie voor je:
- Impliciete semantiek: Een doel met
popover="hint"krijgt een impliciete minimumrol vantooltip. Stelrole="tooltip"NIET zelf in. - Impliciete koppeling: De browser koppelt het bronelement impliciet aan het doel via
aria-describedbyals het doel platte tekst is of viaaria-detailsals het doel interactieve content bevat. Voegaria-describedbyofaria-detailsNIET toe aan de trigger. - Interactieve content: Omdat de koppeling indien nodig overschakelt naar
aria-details, mag het doel interactieve content bevatten (bijvoorbeeld een link in een interessekaart).
2. Positionering met ankerpositionering
Positioneer de hovercard ten opzichte van de trigger met CSS-ankerpositionering. Als je interestfor gebruikt, wordt de trigger een impliciet anker voor de popover.
[popover].hovercard {
/* Position the card below the trigger, aligned with its inline-start edge
and spanning toward the inline-end, flipping if it overflows the viewport. */
position-area: block-end span-inline-end;
position-try-fallbacks: flip-block, flip-inline;
}
3. Animatie bij openen en sluiten
Standaard wordt de interesse getoond en verloren voor toetsenbord- en muisgebruikers met een vertraging van een halve seconde. Zo voorkom je dat de getargete popover wordt geopend terwijl je door een pagina tabt of je muis beweegt, en dat deze wordt gesloten terwijl je je muis van de trigger naar de popover beweegt. Gebruik de CSS-property interest-delay om de timing aan te passen.
[interestfor]{
/* Shorthand for interest-delay-start and interest-delay-end */
interest-delay: 0.2s 0.1s;
}
Als gebruikers waarschijnlijk snel achter elkaar meerdere triggers doorlopen, stel je de eerste vertraging in op 0s als er al een popover is geopend die door interesse is geactiveerd.
:root:has(:interest-source) [interestfor] {
interest-delay-start: 0s;
}
Gebruik @starting-style om de animatie bij binnenkomst te definiëren en transition-behavior: allow-discrete (samen met display en overlay) om de animatie bij verlaten van de bovenste laag te definiëren, waarbij de overgangen achter @media (prefers-reduced-motion: no-preference) worden geplaatst.
[popover].hovercard {
opacity: 0;
transform-origin: top;
}
[popover].hovercard:popover-open {
opacity: 1;
transform: translateY(0) scale(1);
}
@starting-style {
[popover].hovercard:popover-open {
opacity: 0;
transform: translateY(-10px) scale(0.95);
}
}
/* MANDATORY: Respect user preference for reduced motion by selectively applying transitions. */
@media (prefers-reduced-motion: no-preference) {
[popover].hovercard {
transition:
opacity,
transform,
display,
overlay;
transition-duration: 0.4s;
transition-behavior: allow-discrete;
}
}
4. OPTIONEEL: Naadloos overgaan naar de volgende pagina
Je kunt eventueel Cross-Document View Transitions gebruiken om een naadloze morph-animatie te maken tussen de hovercard en de bestemmingspagina. Ga naar Consistente overgangen tussen documenten voor richtlijnen voor de implementatie.
Door overeenkomende view-transition-name-waarden toe te wijzen aan elementen op de aanwijskaart en hun tegenhangers op de volgende pagina, animeert de browser ze in de navigatie. Je kunt meerdere elementen benoemen (bijvoorbeeld titels, afbeeldingen, auteursregels) om een complex morphing-effect met meerdere elementen te maken. Wijs de view-transition-name toe aan elementen in de geopende popover met :popover-open om te voorkomen dat er dubbele view-transition-name's worden gemaakt of dat er linkspecifieke view-transition-name's nodig zijn.
/* Opt-in to cross-document transitions on both pages */
@view-transition {
navigation: auto;
}
/* Don't show transitions when loading this page */
::view-transition-group(*) {
animation-duration: 0s;
}
/* Assign names ONLY when the popover is open to ensure only the correct elements are selected. */
[popover]:popover-open {
view-transition-name: --hovercard;
view-transition-class: morph;
h2 {
view-transition-name: --title;
view-transition-class: morph;
}
img {
view-transition-name: --image;
view-transition-class: morph;
}
}
VERPLICHT: De bestemmingspagina moet blocking="render" en <link rel="expect"> gebruiken om te wachten tot essentiële content is geparseerd voordat het eerste frame wordt gerenderd. Zo zorg je dat de overgang stabiel is en niet flikkert.
<!-- On the destination page: -->
<head>
<!-- Block rendering until the hero image and title are parsed,
ensuring the view transition snapshot is taken at the right time. -->
<link rel="expect" href="#main-content" blocking="render">
</head>
<body>
<main id="main-content">
<h1>The buzz on hummingbirds</h1>
<img src="bird.jpg">
</main>
</body>
/* On the destination page: */
main {
view-transition-name: --hovercard;
view-transition-class: morph;
}
h1 {
view-transition-name: --title;
view-transition-class: morph;
}
img {
view-transition-name: --image;
view-transition-class: morph;
}
/* By default no transition */
::view-transition-group(*) {
animation-duration: 0s;
}
/* Short transition for morphing elements */
::view-transition-group(.morph) {
animation-duration: 0.4s;
}
/* Disable transition if no `old` view */
::view-transition-new(.morph):only-child {
animation-duration: 0s;
}
Reservestrategieën
Omdat de trigger een standaard <a href="...">-link is, kunnen browsers zonder ondersteuning nog steeds navigeren bij een klik als je ervoor kiest de hovercard te behandelen als een progressive enhancement. Als ondersteuning voor aanwijskaarten in meerdere browsers is vereist, laad je polyfills voorwaardelijk, indien nodig.
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.
Fallbacks en browserondersteuning voor aanroepers van interesses
Interesse-invokers moeten voorwaardelijk worden gepolyfilled met het interestfor-polyfillpakket van NPM. Bundel de polyfill liever dan het CDN te gebruiken.
VERPLICHT: Voer een functiedetectie uit door te controleren op de property interestForElement op HTMLButtonElement.prototype en laad de polyfill alleen als native ondersteuning ontbreekt.
<script type="module">
if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
// CDN link only used for example, prefer bundling.
await import("https://unpkg.com/interestfor@latest");
}
</script>
Voorbehouden voor CSS-polyfill
Als je de polyfill gebruikt, verandert de CSS API enigszins om rekening te houden met het gebrek aan ondersteuning voor native pseudoklassen en eigenschappen:
- Pseudoklassen: De polyfill kan geen echte
:interest-source- en:interest-target-pseudoklassen definiëren, dus past in plaats daarvan.interest-source- en.interest-target-klassen toe. VERPLICHT: Combineer ze met:is()om zowel systeemeigen als polyfilled statussen te ondersteunen.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
- CSS-eigenschappen: Voor
interest-delay,interest-delay-starteninterest-delay-endvereist de polyfill het gebruik van CSS-variabelen, omdat deze de aangepaste eigenschappen niet rechtstreeks in alle browsers kan lezen.
/* Adjust the start and end delay for interest invokers */
[interestfor] {
--interest-delay-start: 0.2s;
interest-delay-start: var(--interest-delay-start);
--interest-delay-end: 0.1s;
interest-delay-end: var(--interest-delay-end);
}