( Perceivable / WCAG 1.3.1 )
Definition list item used outside a dl element
What is this issue?
A <dt> (definition term) or <dd> (definition description) element is present in the DOM (the tree structure the browser builds from your HTML that assistive technology reads instead of the visual layout) with no <dl> ancestor above it anywhere. The element keeps its tag name, but neither carries a meaningful role on its own; both are defined specifically as children of a <dl> list.
This most often happens when a component renders <dt>/<dd> markup directly without also rendering the surrounding <dl>: for example, a reusable “key-value row” component built with <dt>/<dd> for convenient default styling, dropped into a page inside a plain <div> instead of a <dl>.
Why does this matter?
The “term” and “description” roles a screen reader announces for <dt> and <dd> are computed based on their relationship to an enclosing <dl>. Strip that ancestor away and the browser has no defined role to assign either element; most screen readers fall back to reading the content as generic, unstructured text, with no indication that one piece of text is meant to define the other.
That loss matters most exactly where <dt>/<dd> tends to get used: glossaries, FAQs, and metadata lists, where the term-definition relationship is the content’s whole structure. Without it, a page that visually reads as a clean glossary sounds, to a screen reader, like a run-on list of sentences with no way to tell where one entry ends and the next begins.
Who is affected?
- Screen reader users: hear term and definition text as flat, unstructured content with no announced relationship between the two, and lose any ability to jump directly between entries.
- Cognitive disabilities: users who scan a glossary or FAQ by relying on the term/definition structure to chunk information lose that chunking entirely when the underlying pairing isn’t announced.
What users experience
Idris uses JAWS on Windows to look up an acronym in a technical documentation site’s terminology reference, which renders each entry as a bare <dt>/<dd> pair inside a plain <div>, because the developer reused a “key-value” component that outputs <dt>/<dd> markup but forgot to also render the <dl> wrapper it was designed to sit inside. JAWS reads “WCAG” immediately followed by “Web Content Accessibility Guidelines” with no term or definition role announced for either, so Idris can’t tell from the announcement alone whether he’s hearing a definition, a random pair of sentences, or a broken heading; he has to reread it twice, working from the content alone, before he’s confident it’s actually a glossary entry.
How do I fix it?
Wrap every <dt>/<dd> pair in a <dl> element. This works because the term and description roles a screen reader announces only apply to <dt> and <dd> when a <dl> ancestor is present: the wrapper isn’t decorative, it’s what activates the roles in the first place.
If your <dt>/<dd> markup comes from a shared component, check that the component either renders its own <dl> wrapper internally or clearly documents that callers must supply one; don’t rely on every usage site remembering to add it. A component that always renders <dt>/<dd> without ever rendering <dl> will reintroduce this exact failure every time it’s reused.
Code Examples
<div class="glossary-entry">
<dt>WCAG</dt>
<dd>Web Content Accessibility Guidelines</dd>
</div><dl class="glossary-entry">
<dt>WCAG</dt>
<dd>Web Content Accessibility Guidelines</dd>
</dl>Only the outer wrapper’s tag changed, from <div> to <dl>; the <dt> and <dd> elements and all their styling classes stayed exactly the same. That single tag change is what activates the term and description roles a screen reader looks for.
Common Mistakes
Mistake: “I wrapped the pair in a div with a glossary-entry class, so it’s structured correctly.” A CSS class name describes intent to another developer reading the code; it does nothing for a screen reader, which only reads actual element roles from the DOM. Only an actual <dl> element activates the term/description roles: no class name, however descriptive, substitutes for it.
Mistake: “The component library I’m using renders dt and dd, so it must handle the dl wrapper too.” Many shared “key-value” or “metadata row” components render only the inner <dt>/<dd> pair and expect the page using them to supply the surrounding <dl>; check the component’s documentation or rendered output directly rather than assuming. This is one of the most common sources of this exact failure in component-based codebases.
Mistake: “A single dt/dd pair doesn’t need a full dl wrapper, that’s only necessary for a list of several.” The term and description roles apply the same way regardless of how many pairs exist inside the <dl>; even a single pair needs the wrapper to be announced correctly. There’s no size threshold below which the structural requirement stops applying.
How RedFlag Detects This
Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s dlitem rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule inspects every <dt> and <dd> element on the page and flags any with no <dl> element among its ancestors.
False negative: the check confirms a <dl> ancestor exists somewhere above the element; it can’t confirm the pairing inside that <dl> is otherwise valid: a <dt>/<dd> pair correctly wrapped in a <dl> alongside other invalid direct children (caught by a separate structural rule) still passes this specific ancestor check. False positive: none typical for this check, since confirming whether any ancestor is a <dl> element is a straightforward DOM walk with a binary outcome. Manual step: for any flagged <dt>/<dd> pair, confirm after adding the <dl> wrapper that a screen reader now announces the term and definition roles correctly, since a structurally present <dl> and an audibly correct announcement are two separate things worth checking independently.
Manual Testing
- Open the page in Chrome or Firefox with NVDA or JAWS running on Windows, or VoiceOver on macOS.
- Navigate to any glossary, FAQ, or metadata section on the page.
- Listen for a “term” role announced before each label and a “definition” role announced before each value.
- If the content is read with no role announced at all (just plain text, one piece after another), inspect the markup in browser dev tools to confirm whether a
<dl>ancestor is actually present above the<dt>/<dd>pair. - Repeat for every glossary-style component on the page, since a shared component can pass in one usage and fail in another if only some instances supply the
<dl>wrapper.
Related WCAG Success Criteria
1.3.1 Info and Relationships: Information, structure, and relationships conveyed through presentation must also be available programmatically. A term and definition that read correctly to a sighted user only satisfy this criterion once the same relationship is expressed through valid <dl>/<dt>/<dd> markup, not through visual proximity or styling alone.
Related Issues
Definition list structure is invalid is the mirror-image failure: instead of <dt>/<dd> existing with no <dl> wrapper, it’s a <dl> that exists but contains the wrong kind of direct children.
List items are not contained within a list element is the same “orphaned item, no container” failure applied to <li> and <ul>/<ol> instead of <dt>/<dd> and <dl>.
List contains an element other than li rounds out the same structural family: a container failing to hold only its valid child type, the counterpart to this rule’s child failing to have a container at all.
References
- W3C Understanding 1.3.1: Info and Relationships
- MDN: The dl element
- WebAIM: Semantic Structure - Description Lists
Frequently asked questions
Does the dt/dd pair need to be a direct child of dl, or just a descendant?
A direct child or grouped inside a div that is itself a direct child of dl both work, since HTML explicitly allows that grouping pattern. What fails this check is a dt or dd with no dl ancestor anywhere above it at all, at any depth.
Can I use dt and dd inside a details/summary element without a dl wrapper?
No, dt and dd still need a dl ancestor regardless of what other elements surround them. If you want a collapsible glossary entry, wrap the dl itself inside the details element rather than substituting details/summary for the dl structure.
Why does removing the dl break things if the dt and dd text is still readable?
The text stays readable, but the term and definition roles that make the pairing meaningful to a screen reader only exist when dt and dd are inside a dl. Without it, the same words are announced as plain, unconnected text with no indication one is defining the other.
Is a single dl with just one dt and one dd pair still worth using, or is that overkill?
It is still worth using. A single term-definition pair benefits from the same structural announcement as a longer glossary, and there is no minimum size requirement for using dl correctly. Using it consistently, even for one pair, avoids reintroducing this rule the next time content is added.
Does this rule apply to dt and dd used inside a custom JavaScript component?
Yes. The requirement is about the final rendered DOM, not how the markup was authored. A component that renders dt and dd without also rendering a dl wrapper around them fails this check the same as hand-written HTML would.