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.

cursor: pointer
on the <summary>
element of its disclosure widget menus. 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.

<summary>
headings could have odd or undesirable effects in Internet Explorer and EdgeHTML.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.
Styling Comment Threads
Comment threads are one of those things that look really simple when executed right. When designing them yourself, you may find that they are rather deceptively simple. There is a lot that goes into designing nice and usable comment threads, and in this article, I will try my best to walk you through the steps to building a comment thread, that is great to look at, and a joy to use.
What makes a good comment thread?
Before diving into designing and writing code, let’s break down what actually makes a good comment thread. A good comment thread will have the following characteristics:
As you can see, that’s quite a lot to consider! There are also some nice-to-haves that we won’t cover in this article, but are certainly good enhancements:
The above features would require at least a bit of JavaScript to pull off. Moreover, depending on the tech stack, these features could just as likely be implemented server side, especially keeping track of the number of votes and flag status of comments. That is why we will focus only on styling the comment threads in this article. With that out of the way, let’s knock out the first set of points and design our comment thread.
A basic comment thread
A comment by itself has a pretty simple structure. Here’s a skeleton of a single comment:
So far, so good. Notice how the replies have an extra margin to the left. This is meant to satisfy the visual hierarchy (point #3 above). The markup for the above structure could look something like this:
There is nothing to explain here — just a bunch of containers with some basic elements. So instead, let’s look at a real-life example, with a comment that also has a reply.
Looks pretty nice, right? The above example satisfies the first three points, so we are already on our way. A few things to understand about the code above:
.sr-only
hides elements on all devices except on screen readers. This makes the voting buttons accessible. If you are using a framework, like Bootstrap or Halfmoon, this class comes automatically packed in.Adding links that jump to comments
Now that we have a basic comment thread going, let’s add a feature to help users quickly scroll to a comment. As mentioned above, this is especially useful when someone wants to jump to the parent comment of a reply.
In order to build it, we need to decide what these links look like. While this is entirely subjective, one particular design I really like is a clickable “border” on the left hand side of the comment, something like this:
In order to accommodate the link, we are pushing the comment body to the right and aligning it with the replies. This design also has the added benefit of reinforcing the hierarchy between the comments, because you can simply look at the number of border links to the left and determine the level of nesting of the comment you are currently reading. And of course, you can also immediately jump to any upper level by clicking on the border links.
To actually create these border links, we need to add anchor elements (
<a href="...">
) inside each of our comments, and style these anchor elements to look like the borders that can be clicked. Let’s add them to our first code example above.Here are a few things to understand about the changes made:
.comment-heading
(which contains the votes, author, and time added) has a fixedheight
of50px
. Therefore, by giving the border links the properties ofposition:absolute
,top: 50px
, andheight: calc(100% - 50px)
, we are ensuring that they will start right below the heading, and go all the way down to the end of the comment. If you are not familiar with thecalc()
function, you can read this cool guide by Chris.width
of12px
along with aborder-width
of4px
on the left and right. This means that while the visible area is only4px
wide, the actual clickable area is12px
wide. A wider surface is to help the users have an easier time actually positioning their pointers on the link and clicking it, because4px
is a little too narrow, but anything wider would look off visually.With all that, we have knocked out the first four of the points mentioned above. Let’s add more comments to the code example to see how it would look.
Allowing users to hide/show comments with a click
At this point, we have a pretty darn satisfactory comment thread. This design by itself can work for quite a lot of real life use cases. However, let’s go one step farther and add our toggle feature, i.e. hiding and showing comments with a click.
The quickest and easiest way to allow users to hide and show comments with a click is to make use of the
<details>
and<summary>
elements. To put it simply, the visibility of the entire<details>
can be toggled by clicking on the<summary>
. There is no JavaScript involved, and these tags are supported by ~96% of browsers at this moment. Once again, if you are unfamiliar with these concepts, you can learn more in yet another article from Chris.Anyway, to actually implement this, we need to make the following changes to our code:
<div>
to<details>
, i.e. all the elements with the class.comment
will now be a<details>
element..comment-heading
) inside of a<summary>
tag.Seems easy enough. Anyway, here’s our new implementation:
Here are the final things to understand about the changes made:
<details>
, and they are all given theopen
attribute, so they are visible (i.e. open) by default.<summary>
tags. The default arrow is also removed.<details>
element has theopen
attribute or not. The text itself is a simple pseudo-element created using the::after
selector. Moreover, closed comments also have a border on the bottom to show the users that there is more to see.@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {...}
selector. This is a hack that only targets Internet Explorer. You see, IE does not support<details>
, so they are always open by default there. By removing the text and resetting the cursor, IE users will see a regular comment thread, only without the ability to actually toggle the comment visibility. So, no harm, no foul.Honorable mentions
Before we end this article, let’s talk about a few more things that is worth considering when designing comment threads in real-life applications.
What if the comment thread has no comments to show?
This may be a very simple problem to solve, but it is often easy to overlook these simple things. If a comment thread does not have any comments (empty state), we need to communicate that clearly to the user. A simple paragraph containing the line “There are no comments yet.” along with a form containing a text box and submit button can go a very long way, and should be the bare minimum when dealing with empty states. If you want to go the extra mile, you can also have a nice image (that communicates the message) accompanying the form.
How to handle the form for replying to a comment?
When it comes to the form for replying to a comment, different websites have different implementations. Some use the old fashioned way of redirecting users to a new page which contains the form — a simple text box with a submit button. Others open up a form right within the comment thread itself, usually with a simple toggle. The latter paradigm obviously requires JavaScript, but it is more more popular these days. For instance, in our example above, we could have a simple form which can be toggled by clicking on the Reply button, like so:
In the above example, we added simple forms inside the comment bodies, and gave them the class
.d-none
by default, which setsdisplay: none;
and hides them from view. Thanks to the simple event listener, any button with the attributesdata-toggle="reply-form"
anddata-target="{{ comment_reply_form_id }}
can be clicked to toggle the visibility of the forms. This is a very simple example of handling the reply forms with ease.Where to place a new reply after a user is done posting it?
Let’s say a user replies to a comment using a form similar to the one shown above. Do you show it above the existing replies or below it? The answer is that it should always be shown above the other replies right after the user posts it for the first time. When a person fills out a form and submits it, they want immediate feedback to tell them that it worked. Therefore, by placing the new reply above the others, we are providing this feedback to the user without them needing to scroll down. On subsequent loads, you can of course arrange your comment replies according to whatever algorithm you see fit for your website.
Handling Markdown and code blocks
Many websites, particularly developer blogs, need to support markdown and code blocks in their comments. This is a much bigger discussion, perhaps warranting a dedicated article on this topic. However, for the sake of this article, let’s just say that there are plenty of Markdown editors out there that you can attach to a text box quite easily. Most of them work with JavaScript, so they should be fairly easy to integrate in our examples. One such plugin is markdown-it, which has a permissive MIT license. You can also look into WYSIWYG editors, which also serve a very similar purpose when it comes to comments on the web.
Spam prevention and user authentication
If you give users a form to provide their inputs, you can guarantee that you will find spam coming your way, so this is obviously an issue to address when building comment threads. A great way to reduce spam is to use services like reCAPTCHA from Google. For instance, in our example above, a reCAPTCHA box could be placed right below the Submit button in the reply forms. This would protect our website from abuse.
Another way to prevent spam is to only allow authenticated users to post comments, i.e. a user must have an account and be logged in to post a comment. Every comment would obviously be linked to an account, so this has the benefit of allowing moderators to handle users who continuously post spam or low effort content. In terms of handling it in the UI, a great way of doing it is by redirecting users to a login page when they click on the Reply or Post comment button if they are not logged in. Once they complete the authentication process, we can simply redirect them back to the comment thread and open up the form.
And we are done! We have fulfilled all five of our points, and have designed a nice-looking comment thread that is highly usable and accessible, with some cool features like jumping to comments, and togging the visibility of each comment. We also talked about forms inside comment threads, and discussed other things to consider in real-life applications. The bulk of our comment thread works using only CSS (no JavaScript), which goes to show you how far CSS has actually come.
The post Styling Comment Threads appeared first on CSS-Tricks.
You can support CSS-Tricks by being an MVP Supporter.
CSS-Tricks