<u-details>
<u-details> lets you open and close content when clicking on a child <u-summary>.
You can use it to make things like accordions, expandables, FAQs, dropdowns, etc.
Quick intro:
- Use
<u-summary>as a direct child - this is the label - Use any other content in
<u-details>- this will hide/show - Use the
openattribute on<u-details>to change state - MDN Web Docs: <details> (HTMLDetailsElement) / <summary> (HTMLElement)
Example
<u-details> <u-summary>Details</u-summary> Something small enough to escape casual notice. </u-details>
Install
npm add -S @u-elements/u-detailspnpm add -S @u-elements/u-detailsyarn add @u-elements/u-detailsbun add -S @u-elements/u-details<script type="module" src="https://unpkg.com/@u-elements/u-details@latest/dist/u-details.js"></script>Attributes and props
<u-details>
- Attributes: all global HTML attributes such as
id,class,data-openshows content if attribute is present. By default this attribute is absent which means the content is hidden. Note: Settingopen="false"will not work as intended, asopenis a boolean attribute you should provide or remove entirely.nameenables multiple<u-details>elements to be connected, with only one open at a time.
- DOM interface:
HTMLDetailsElementHTMLDetailsElement.openreturnstrueoffalsereflecting the stateHTMLDetailsElement.namereturns the correspondingnameattribute
<u-summary>
- Attributes: all global HTML attributes such as
id,class,data- - DOM interface:
HTMLElement
Events
In addition to the usual events supported by HTML elements, the <u-details> element dispatches a toggle event after the open state is changed:
details.addEventListener('toggle', (event) => {
if (details.open) {
// the element was toggled open
} else {
// the element was toggled closed
}
});Styling and animating
The <summary> element is normally styled with display: list-item, but <u-summary> uses display: block. This change prevents screen readers from announcing the open/close triangle and resolves the double-announcement bug in iOS Safari VoiceOver. To change the triangle and its announcement, you can apply the u-summary::before { display: none } to hide or u-summary::before { all: unset; ...your-styling-here } to restyle.
<details>/<u-details> hides its content - the ::details-content pseudo element. Since custom pseudo selector are not
possible to replicate in custom elements, u-details instead provide ::part(details-content).
If your browser supports interpolate-size, you can animate open/close with pure css:
<u-details class="animate">
<u-summary>Details animating if supported</u-summary>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent interdum diam quis eros sollicitudin, et scelerisque arcu malesuada. Nunc pellentesque eleifend nulla a convallis.
</u-details>
<style>
.animate{
@media (prefers-reduced-motion: no-preference) {
interpolate-size: allow-keywords;
}
&::part(details-content) {
block-size: 0;
overflow-y: clip;
transition: content-visibility 500ms allow-discrete,
height 500ms;
}
&[open]::part(details-content) {
height: auto;
}
}
</style>
Find-in-page
Even when a <details>/<u-details> element is closed, all of its content remains discoverable through the find-in-page search feature (e.g., Ctrl or Command + F keys). This behavior is supported by various browsers. If a user conducts a search for content within a details element, it will automatically open and trigger the toggle event.
Server side rendering
You can server side render <u-details> by using Declarative Shadow DOM.
Styling and markup needed is exported as UHTMLDetailsShadowRoot. Example:
import { UHTMLDetailsShadowRoot } from '@u-elements/u-details';
const renderToStaticMarkup = (title: string, content: string) =>
`<u-details>
${UHTMLDetailsShadowRoot}
<u-summary slot="summary">${title}</u-summary>
${content}
</u-details>`Accessibility (tested 16.09.24)
| Screen reader | <details> | <u-details> |
|---|---|---|
| VoiceOver (Mac) + Chrome | ✅ | ✅ |
| VoiceOver (Mac) + Edge | ✅ | ✅ |
| VoiceOver (Mac) + Firefox | ✅ | ✅ |
| VoiceOver (Mac) + Safari | ❌ Does not announce state + looses screen reader focus | ✅ |
| VoiceOver (iOS) + Chrome | ✅ | ✅ |
| VoiceOver (iOS) + Safari | ✅ | ✅ |
| Jaws (PC) + Chrome | ✅ | ✅ |
| Jaws (PC) + Edge | ✅ | ✅ |
| Jaws (PC) + Firefox | ✅ | ✅ |
| NVDA (PC) + Chrome | ✅ | ✅ |
| NVDA (PC) + Edge | ✅ | ✅ |
| NVDA (PC) + Firefox | ✅ | ✅ |
| Narrator (PC) + Chrome | ❌ Does not announce state | ✅ |
| Narrator (PC) + Edge | ✅ | ✅ |
| Narrator (PC) + Firefox | ✅ | ✅ |
| TalkBack (Android) + Chrome | ❌ Does not announce role | ✅ |
| TalkBack (Android) + Firefox | ❌ Does not announce role or state on focus | ✅ |
| TalkBack (Android) + Samsung Internet | ❌ Does not announce role | ✅ |
Specifications
- DOM interface: HTMLDetailsElement
- HTML Standard: The <details> element
- HTML Standard: The <summary> element
Changelog
- 0.1.5: Add
tabindex="-1"to content when closed to preved Firefox from making it a tabstop - 0.1.4: Remove
aria-labelledby="summary-id"to reduce information duplication - 0.1.3: Enable declarative shadow root support and export
UHTMLDetailsShadowRootfor easier server side rendering - 0.1.2: Add
role="group"to align with semantics