Home > Designing, Others > The Different (and Modern) Ways to Toggle Content

The Different (and Modern) Ways to Toggle Content

November 8th, 2024 Leave a comment Go to comments

If all you have is a hammer, everything looks like a nail.

Abraham Maslow

It’s easy to default to what you know. When it comes to toggling content, that might be reaching for display: none or opacity: 0 with some JavaScript sprinkled in. But the web is more “modern” today, so perhaps now is the right time to get a birds-eye view of the different ways to toggle content — which native APIs are actually supported now, their pros and cons, and some things about them that you might not know (such as any pseudo-elements and other non-obvious stuff).

So, let’s spend some time looking at disclosures (

and

), the Dialog API, the Popover API, and more. We’ll look at the right time to use each one depending on your needs. Modal or non-modal? JavaScript or pure HTML/CSS? Not sure? Don’t worry, we’ll go into all that.

Disclosures (

and

)

Use case: Accessibly summarizing content while making the content details togglable independently, or as an accordion.

CodePen Embed Fallback

Going in release order, disclosures — known by their elements as

and

— marked the first time we were able to toggle content without JavaScript or weird checkbox hacks. But lack of web browser support obviously holds new features back at first, and this one in particular came without keyboard accessibility. So I’d understand if you haven’t used it since it came to Chrome 12 way back in 2011. Out of sight, out of mind, right?

Here’s the low-down:

  • It’s functional without JavaScript (without any compromises).
  • It’s fully stylable without appearance: none or the like.
  • You can hide the marker without non-standard pseudo-selectors.
  • You can connect multiple disclosures to create an accordion.
  • Aaaand… it’s fully animatable, as of 2024.

Marking up disclosures

What you’re looking for is this:

<details>
  <summary>Content summary (always visible)</summary>
  Content (visibility is toggled when summary is clicked on)
</details>

Behind the scenes, the content’s wrapped in a pseudo-element that as of 2024 we can select using ::details-content. To add to this, there’s a ::marker pseudo-element that indicates whether the disclosure’s open or closed, which we can customize.

With that in mind, disclosures actually look like this under the hood:

<details>
  <summary><::marker></::marker>Content summary (always visible)</summary>
  <::details-content>
      Content (visibility is toggled when summary is clicked on)
  </::details-content>
</details>

To have the disclosure open by default, give

the open attribute, which is what happens behind the scenes when disclosures are opened anyway.

<details open> ... </details>

Styling disclosures

Let’s be real: you probably just want to lose that annoying marker. Well, you can do that by setting the display property of

to anything but list-item:

summary {
  display: block; /* Or anything else that isn't list-item */
}
CodePen Embed Fallback

Alternatively, you can modify the marker. In fact, the example below utilizes Font Awesome to replace it with another icon, but keep in mind that ::marker doesn’t support many properties. The most flexible workaround is to wrap the content of

in an element and select it in CSS.

<details>
  <summary><span>Content summary</span></summary>
  Content
</details>
details {
  
  /* The marker */
  summary::marker {
    content: "f150";
    font-family: "Font Awesome 6 Free";
  }

  /* The marker when <details> is open */
  &[open] summary::marker {
    content: "f151";
  }
  
  /* Because ::marker doesn’t support many properties */
  summary span {
    margin-left: 1ch;
    display: inline-block;
  }
  
}
CodePen Embed Fallback

Creating an accordion with multiple disclosures

CodePen Embed Fallback

To create an accordion, name multiple disclosures (they don’t even have to be siblings) with a name attribute and a matching value (similar to how you’d implement ):

<details name="starWars" open>
  <summary>Prequels</summary>
  <ul>
    <li>Episode I: The Phantom Menace</li>
    <li>Episode II: Attack of the Clones</li>
    <li>Episode III: Revenge of the Sith</li>
  </ul>
</details>

<details name="starWars">
  <summary>Originals</summary>
  <ul>
    <li>Episode IV: A New Hope</li>
    <li>Episode V: The Empire Strikes Back</li>
    <li>Episode VI: Return of the Jedi</li>
  </ul>
</details>

<details name="starWars">
  <summary>Sequels</summary>
  <ul>
    <li>Episode VII: The Force Awakens</li>
    <li>Episode VIII: The Last Jedi</li>
    <li>Episode IX: The Rise of Skywalker</li>
  </ul>
</details>

Using a wrapper, we can even turn these into horizontal tabs:

CodePen Embed Fallback
<div> <!-- Flex wrapper -->
  <details name="starWars" open> ... </details>
  <details name="starWars"> ... </details>
  <details name="starWars"> ... </details>
</div>
div {
  gap: 1ch;
  display: flex;
  position: relative;

  details {
    min-height: 106px; /* Prevents content shift */
      
    &[open] summary,
    &[open]::details-content {
      background: #eee;
    }

    &[open]::details-content {
      left: 0;
      position: absolute;
    } 
  }
}

…or, using 2024’s Anchor Positioning API, vertical tabs (same HTML):

div {
  
  display: inline-grid;
  anchor-name: --wrapper;

  details[open] {
      
    summary,
    &::details-content {
      background: #eee;
    }

    &::details-content {
      position: absolute;
      position-anchor: --wrapper;
      top: anchor(top);
      left: anchor(right);
    } 
  }
}
CodePen Embed Fallback

If you’re looking for some wild ideas on what we can do with the Popover API in CSS, check out John Rhea’s article in which he makes an interactive game solely out of disclosures!

Adding JavaScript functionality

Want to add some JavaScript functionality?

// Optional: select and loop multiple disclosures
document.querySelectorAll("details").forEach(details => {
  details.addEventListener("toggle", () => {
    // The disclosure was toggled
    if (details.open) {
      // The disclosure was opened
    } else {
      // The disclosure was closed
    }
  });    
});

Creating accessible disclosures

Disclosures are accessible as long as you follow a few rules. For example,

is basically a , meaning that its content is announced by screen readers when in focus. If there isn’t a

or

isn’t a direct child of

then the user agent will create a label for you that normally says “Details” both visually and in assistive tech. Older web browsers might insist that it be the first child, so it’s best to make it so.

To add to this,

has the role of button, so whatever’s invalid inside a
Categories: Designing, Others Tags:
  1. No comments yet.
You must be logged in to post a comment.