In the not-too-distant past, even basic accordion-like interactions required JavaScript event listeners or some CSS… trickery. And, depending on the solution used, editing the underlying HTML could get complicated.
Now, the <details>
and <summary>
elements (which combine to form what’s called a “disclosure widget”) have made creation and maintenance of these components relatively trivial.
At my job, we use them for things like frequently asked questions.
There are a couple of issues to consider
Because expand-and-collapse interactivity is already baked into the <details>
and <summary>
HTML tags, you can now make disclosure widgets without any JavaScript or CSS. But you still might want some. Left unstyled, <details>
disclosure widgets present us with two issues.
Issue 1: The <summary>
cursor
Though the <summary>
section invites interaction, the element’s default cursor is a text selection icon rather than the pointing finger you may expect:
Issue 2: Nested block elements in <summary>
Nesting a block-level element (e.g. a heading) inside a <summary>
element pushes that content down below the arrow marker, rather than keeping it inline:
The CSS Reset fix
To remedy these issues, we can add the following two styles to the reset section of our stylesheets:
details summary { cursor: pointer;
} details summary > * { display: inline;
}
Read on for more on each issue and its respective solution.
Changing the <summary>
cursor value
When users hover over an element on a page, we always want them to see a cursor “that reflects the expected user interaction on that element.”
We touched briefly on the fact that, although <summary>
elements are interactive (like a link or form button), its default cursor is not the pointing finger we typically see for such elements. Instead, we get the text
cursor, which we usually expect when entering or selecting text on a page.
To fix this, switch the cursor’s value to pointer
:
details summary { cursor: pointer;
}
Some notable sites already include this property when they style <details>
elements. The MDN Web Docs page on the element itself does exactly that. GitHub also uses disclosure widgets for certain items, like the actions to watch, star and fork a repo.
I’m guessing the default cursor: text
value was chosen to indicate that the summary text can (along with the rest of a disclosure widget’s content) be selected by the user. But, in most cases, I feel it’s more important to indicate that the <summary>
element is interactive.
Summary text is still selectable, even after we’ve changed the cursor value from text
to pointer
. Note that changing the cursor only affects appearance, and not its functionality.
Displaying nested <summary>
contents inline
Inside each <summary>
section of the FAQ entries I shared earlier, I usually enclose the question in an appropriate heading tag (depending on the page outline):
<details> <summary> <h3>Will my child's 504 Plan be implemented?</h3> </summary> <p>Yes. Similar to the Spring, case managers will reach out to students.</p>
</details>
Nesting a heading inside <summary>
can be helpful for a few reasons:
- Consistent visual styling. I like my FAQ questions to look like other headings on my pages.
- Using headings keeps the page structure valid for users of Internet Explorer and pre-Chromium versions of Edge, which don’t support
<details>
elements. (In these browsers, such content is always visible, rather than interactive.) - Proper headings can help users of assistive technologies navigate within pages. (That said, headings within
<summary>
elements pose a unique case, as explained in detail below. Some screen readers interpret these headings as what they are, but others don’t.)
Headings vs. buttons
Keep in mind that the <summary>
element is a bit of an odd duck. It operates like a button in many ways. In fact, it even has implicit role=button
ARIA mapping. But, very much unlike buttons, headings are allowed to be nested directly inside <summary>
elements.
This poses us — and browser and assistive technology developers — with a contradiction:
- Headings are permitted in
<summary>
elements to provide in-page navigational assistance. - Buttons strip the semantics out of anything (like headings) nested within them.
Unfortunately, assistive technologies are inconsistent in how they’ve handled this situation. Some screen-reading technologies, like NVDA and Apple’s VoiceOver, do acknowledge headings inside <summary>
elements. JAWS, on the other hand, does not.
What this means for us is that, when we place a heading inside a <summary>
, we can style the heading’s appearance. But we cannot guarantee our heading will actually be interpreted as a heading!
In other words, it probably doesn’t hurt to put a heading there. It just may not always help.
Inline all the things
When using a heading tag (or another block element) directly inside our <summary>
, we’ll probably want to change its display
style to inline
. Otherwise, we’ll get some undesired wrapping, like the expand/collapse arrow icon displayed above the heading, instead of beside it.
We can use the following CSS to apply a display
value of inline
to every heading — and to any other element nested directly inside the <summary>
:
details summary > * { display: inline;
}
A couple notes on this technique. First, I recommend using inline
, and not inline-block
, as the line wrapping issue still occurs with inline-block
when the heading text extends beyond one line.
Second, rather than changing the display
value of the nested elements, you might be tempted to replace the <summary>
element’s default display: list-item
value with display: flex
. At least I was! However, if we do this, the arrow marker will disappear. Whoops!
Bonus tip: Excluding Internet Explorer from your styles
I mentioned earlier that Internet Explorer and pre-Chromium (a.k.a. EdgeHTML) versions of Edge don’t support <details>
elements. So, unless we’re using polyfills for these browsers, we may want to make sure our custom disclosure widget styles aren’t applied for them. Otherwise, we end up with a situation where all our inline styling garbles the element.
Plus, the <summary>
element is no longer interactive when this happens, meaning the cursor’s default text
style is more appropriate than pointer
.
If we decide that we want our reset styles to target only the appropriate browsers, we can add a feature query that prevents IE and EdgeHTML from ever having our styles applied. Here’s how we do that using @supports
to detect a feature only those browsers support:
@supports not (-ms-ime-align: auto) { details summary { cursor: pointer; } details summary > * { display: inline; } /* Plus any other <details>/<summary> styles you want IE to ignore.
}
IE actually doesn’t support feature queries at all, so it will ignore everything in the above block, which is fine! EdgeHTML does support feature queries, but it too will not apply anything within the block, as it is the only browser engine that supports -ms-ime-align
.
The main caveat here is that there are also a few older versions of Chrome (namely 12-27) and Safari (macOS and iOS versions 6-8) that do support <details>
but don’t support feature queries. Using a feature query means that these browsers, which account for about 0.06% of global usage (as of January 2021), will not apply our custom disclosure widget styles, either.
Using a @supports selector(details)
block, instead of @supports not (-ms-ime-align: auto)
, would be an ideal solution. But selector queries have even less browser support than property-based feature queries.
Final thoughts
Once we’ve got our HTML structure set and our two CSS reset styles added, we can spruce up all our disclosure widgets however else we like. Even some simple border and background color styles can go a long way for aesthetics and usability. Just know that customizing the <summary>
markers can get a little complicated!
The post Two Issues Styling the Details Element and How to Solve Them appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.