Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      June 1, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      June 1, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      June 1, 2025

      How To Prevent WordPress SQL Injection Attacks

      June 1, 2025

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025

      A week of hell with my Windows 11 PC really makes me appreciate the simplicity of Google’s Chromebook laptops

      June 1, 2025

      Elden Ring Nightreign Night Aspect: How to beat Heolstor the Nightlord, the final boss

      June 1, 2025

      New Xbox games launching this week, from June 2 through June 8 — Zenless Zone Zero finally comes to Xbox

      June 1, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Student Record Android App using SQLite

      June 1, 2025
      Recent

      Student Record Android App using SQLite

      June 1, 2025

      When Array uses less memory than Uint8Array (in V8)

      June 1, 2025

      Laravel 12 Starter Kits: Definite Guide Which to Choose

      June 1, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025
      Recent

      My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

      June 1, 2025

      A week of hell with my Windows 11 PC really makes me appreciate the simplicity of Google’s Chromebook laptops

      June 1, 2025

      Elden Ring Nightreign Night Aspect: How to beat Heolstor the Nightlord, the final boss

      June 1, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Working With Multiple CSS Anchors and Popovers Inside the WordPress Loop

    Working With Multiple CSS Anchors and Popovers Inside the WordPress Loop

    February 19, 2025

    I know, super niche, but it could be any loop, really. The challenge is having multiple tooltips on the same page that make use of the Popover API for toggling goodness and CSS Anchor Positioning for attaching a tooltip to its respective anchor element.

    There’s plenty of moving pieces when working with popovers:

    • A popover needs an ID (and an accessible role while we’re at it).
    • A popovertarget needs to reference that ID.
    • IDs have to be unique for semantics, yes, but also to hook a popover into a popovertarget.

    That’s just the part dealing with the Popover API. Turning to anchors:

    • An anchor needs an anchor-name.
    • A target element needs to reference that anchor-name.
    • Each anchor-name must be unique to attach the target to its anchor properly.

    The requirements themselves are challenging. But it’s more challenging working inside a loop because you need a way to generate unique IDs and anchor names so everything is hooked up properly without conflicting with other elements on the page. In WordPress, we query an array of page objects:

    $property_query = new WP_Query(array(
      'post_type' => 'page',
      'post_status' => 'publish',
      'posts_per_page' => -1, // Query them all!
      'orderby' => 'title',
      'order' => "ASC"
    ));

    Before we get into our while() statement I’d like to stub out the HTML. This is how I want a page object to look inside of its container:

    <div class="almanac-group">
      <div class="group-letter"><a href="#">A</a></div>
      <div class="group-list">
        <details id="" class="group-item">
          <summary>
            <h2><code>accent-color</code></h2>
          </summary>
        </details>
    
        <!-- Repeat for all properties -->
      </div>
    </div>
    
    <!-- Repeat for the entire alphabet -->

    OK, let’s stub out the tooltip markup while we’re here, focusing just inside the <details> element since that’s what represents a single page.

    <details id="page" class="group-item">
      <summary>
        <h2><code>accent-color</code></h2>
    
        <span id="tooltip" class="tooltip">
          <!-- Popover Target and Anchor -->
          <button class="info-tip-button" aria-labelledby="experimental-label" popovertarget="experimental-label">  
            <!-- etc. -->
          </button>
    
          <!-- Popover and Anchor Target -->
          <div popover id="experimental-label" class="info-tip-content" role="tooltip">
            Experimental feature
          </div>
        </span>
      </summary>
    </details>

    With me so far? We’ll start with the Popover side of things. Right now we have a <button> that is connected to a <div popover>. Clicking the former toggles the latter.

    CodePen Embed Fallback

    Styling isn’t really what we’re talking about, but it does help to reset a few popover things so it doesn’t get that border and sit directly in the center of the page. You’ll want to check out Michelle Barker’s article for some great tips that make this enhance progressively.

    .info-tip {
      position: relative; /* Sets containment */
    
      /* Bail if Anchor Positioning is not supported */
      [popovertarget] {
        display: none;
      }
    
      /* Style things up if Anchor Positioning is supported */
      @supports (anchor-name: --infotip) {
    
        [popovertarget] {
          display: inline;
          position: relative;
        }
    
        [popover] {
          border: 0; /* Removes default border */
          margin: 0; /* Resets placement */
          position: absolute; /* Required */
      }
    }

    This is also the point at which you’ll want to start using Chrome because Safari and Firefox are still working on supporting the feature.

    CodePen Embed Fallback

    We’re doing good! The big deal at the moment is positioning the tooltip’s content so that it is beside the button. This is where we can start working with Anchor Positioning. Juan Diego’s guide is the bee’s knees if you’re looking for a deep dive. The gist is that we can connect an anchor to its target element in CSS. First, we register the <button> as the anchor element by giving it an anchor-name. Then we anchor the <div popover> to the <button> with position-anchor and use the anchor() function on its inset properties to position it exactly where we want, relative to the <button>:

    .tooltip {
      position: relative; /* Sets containment */
    
      /* Bail if Anchor Positioning is not supported */
      [popovertarget] {
        display: none;
      }
    
      /* Style things up if Anchor Positioning is supported */
      @supports (anchor-name: --tooltip) {
    
        [popovertarget] {
          anchor-name: --tooltip;
          display: inline;
          position: relative;
        }
    
        [popover] {
          border: 0; /* Removes default border */
          margin: 0; /* Resets placement */
          position: absolute; /* Required */
          position-anchor: --tooltip;
          top: anchor(--tooltip -15%);
          left: anchor(--tooltip 110%);
        }
      }
    }
    CodePen Embed Fallback

    This is exactly what we want! But it’s also where things more complicated when we try to add more tooltips to the page. Notice that both buttons want to cull the same tooltip.

    CodePen Embed Fallback

    That’s no good. What we need is a unique ID for each tooltip. I’ll simplify the HTML so we’re looking at the right spot:

    <details>
      <!-- ...  -->
    
        <!-- Popover Target and Anchor -->
        <button class="info-tip-button" aria-labelledby="experimental-label" popovertarget="experimental-label">  
          <!-- ... -->
        </button>
    
        <!-- Popover and Anchor Target -->
        <div popover id="experimental-label" class="info-tip-content" role="tooltip">
          Experimental feature
        </div>
        
        <!-- ... -->
    
    </details>

    The popover has an ID of #experimental-label. The anchor references it in the popovertarget attribute. This connects them but also connects other tooltips that are on the page. What would be ideal is to have a sequence of IDs, like:

    <!-- Popover and Anchor Target -->
    <div popover id="experimental-label-1" class="info-tip-content" role="tooltip"> ... </div>
    <div popover id="experimental-label-2" class="info-tip-content" role="tooltip"> ... </div>
    <div popover id="experimental-label-3" class="info-tip-content" role="tooltip"> ... </div>
    <!-- and so on... -->

    We can make the page query into a function that we call:

    function letterOutput($letter, $propertyID) {
      $property_query = new WP_Query(array(
        'post_type' => 'page',
        'post_status' => 'publish',
        'posts_per_page' => -1, // Query them all!
        'orderby' => 'title',
        'order' => "ASC"
      ));
    }

    And when calling the function, we’ll take two arguments that are specific only to what I was working on. If you’re curious, we have a structured set of pages that go Almanac → Type → Letter → Feature (e.g., Almanac → Properties → A → accent-color). This function outputs the child pages of a “Letter” (i.e., A → accent-color, anchor-name, etc.). A child page might be an “experimental” CSS feature and we’re marking that in the UI with tooltops next to each experimental feature.

    We’ll put the HTML into an object that we can return when calling the function. I’ll cut it down for brevity…

    $html .= '<details id="page" class="group-item">';
    $html .=   '<summary>';
    $html .=     '<h2><code>accent-color</code></h2>';
    $html .=     '<span id="tooltip" class="tooltip">';
    $html .=       '<button class="info-tip-button" aria-labelledby="experimental-label" popovertarget="experimental-label">  ';
    // ...
    $html .=       '</button>';
    $html .=       '<div popover id="experimental-label" class="info-tip-content" role="tooltip">';
    // ...
    $html .=       '</div>';
    $html .=     '</span>';
    $html .=   '</summary>';
    $html .= '</details>';
    
    return $html;

    WordPress has some functions we can leverage for looping through this markup. For example, we can insert the_title() in place of the hardcoded post title:

    $html .= '<h2><code>' . get_the_title(); . '</code></h2>';

    We can also use get_the_id() to insert the unique identifier associated with the post. For example, we can use it to give each <details> element a unique ID:

    $html .= '<details id="page-' . get_the_id(); . '" class="group-item">';

    This is the secret sauce for getting the unique identifiers needed for the popovers:

    // Outputs something like `id="experimental-label-12345"`
    $html .= '<div popover id="experimental-label-' . get_the_id(); . '" class="info-tip-content" role="tooltip">';

    We can do the exact same thing on the <button> so that each button is wired to the right popover:

    $html .= '<button class="info-tip-button" aria-labelledby="experimental-label-' . get_the_id(); . '" popovertarget="experimental-label">  ';

    We ought to do the same thing to the .tooltip element itself to distinguish one from another:

    $html .= '<span id="tooltip-' . get_the_id(); . '" class="tooltip">';

    I can’t exactly recreate a WordPress instance in a CodePen demo, but here’s a simplified example with similar markup:

    CodePen Embed Fallback

    The popovers work! Clicking either one triggers its respective popover element. The problem you may have realized is that the targets are both attached to the same anchor element — so it looks like we’re triggering the same popover when clicking either button!

    This is the CSS side of things. What we need is a similar way to apply unique identifiers to each anchor, but as dashed-idents instead of IDs. Something like this:

    /* First tooltip */
    #info-tip-1 {
      [popovertarget] {
        anchor-name: --infotip-1;
      }
    
      [popover] {
        position-anchor: --infotip-1;
        top: anchor(--infotip-1 -15%);
        left: anchor(--infotip-1 100%);
      }
    }
    
    /* Second tooltip */
    #info-tip-2 {
      [popovertarget] {
        anchor-name: --infotip-1;
      }
    
      [popover] {
        position-anchor: --infotip-1;
        top: anchor(--infotip-1 -15%);
        left: anchor(--infotip-1 100%);
      }
    }
    
    /* Rest of tooltips... */

    This is where I feel like I had to make a compromise. I could have leveraged an @for loop in Sass to generate unique identifiers but then I’d be introducing a new dependency. I could also drop a <style> tag directly into the WordPress template and use the same functions to generate the same post identifiers but then I’m maintaining styles in PHP.

    I chose the latter. I like having dashed-idents that match the IDs set on the .tooltip and popover. It ain’t pretty, but it works:

    $html .= '
    <style>
      #info-tip-' . get_the_id() . ' {
        [popovertarget] {
          anchor-name: --infotip-' . get_the_id() . ';
        }
    
        [popover] {
          position-anchor: --infotip-' . get_the_id() . ';
          top: anchor(--infotip-' . get_the_id() . ' -15%);
          left: anchor(--infotip-' . get_the_id() . ' 100%);
        }
      }
    </style>'

    We’re technically done!

    CodePen Embed Fallback

    The only thing I had left to do for my specific use case was add a conditional statement that outputs the tooltip only if it is marked an “Experimental Feature” in the CMS. But you get the idea.

    Isn’t there a better way?!

    Yes! But not quite yet. Bramus proposed a new ident() function that, when it becomes official, will generate a series of dashed idents that can be used to name things like the anchors I’m working with and prevent those names from colliding with one another.

    <div class="group-list">
      <details id="item-1" class="group-item">...</details>
      <details id="item-2" class="group-item">...</details>
      <details id="item-3" class="group-item">...</details>
      <details id="item-4" class="group-item">...</details>
      <details id="item-5" class="group-item">...</details>
      <!-- etc. -->
    </div>
    /* Hypothetical example — does not work! */
    .group-item { 
      anchor-name: ident("--infotip-" attr(id) "-anchor");
      /* --infotip-item-1-anchor, --infotip-item-2-anchor, etc. */
    }

    Let’s keep our fingers crossed for that to hit the specifications soon!


    Working With Multiple CSS Anchors and Popovers Inside the WordPress Loop originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleRilasciata Rhino Linux 2025.2: Un Aggiornamento Ricco di Correzioni e Miglioramenti
    Next Article Best Free and Open Source Alternatives to Microsoft Magnifier

    Related Posts

    News & Updates

    My top 5 must-play PC games for the second half of 2025 — Will they live up to the hype?

    June 1, 2025
    News & Updates

    A week of hell with my Windows 11 PC really makes me appreciate the simplicity of Google’s Chromebook laptops

    June 1, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    3 Essential Design Trends, September 2024

    Development

    The Secrets of Hidden AI Training on Your Data

    Development

    Microsoft Research Introduces MMInference to Accelerate Pre-filling for Long-Context Vision-Language Models

    Machine Learning

    Finalmente Rilasciato GIMP 3.0: Una Rivoluzione nell’Editing delle Immagini

    Linux

    Highlights

    Resume Worded Review: Is It Worth the Price Tag?

    July 1, 2024

    I wrote this Resume Worded review to explore the tools this AI platform offers. Considering…

    People + AI – The Future of the Workforce

    November 1, 2024

    CVE-2025-1050 – Sonos Era 300 Remote Code Execution (RCE) Vulnerability

    April 23, 2025

    You wanted to try OpenAI’s SearchGPT? It’s time to look for AI alternatives

    August 19, 2024
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.