Two headings, one idea
A common front-end pattern is a small label or kicker above a larger heading. It shows up in cards, page subsections, and article intros. The question is how to mark that up without mangling semantics or stuffing the document outline with extra heading levels.
Three visual setups, one underlying question
The context matters less than the structure. Consider a small overline above a section title, a product name with a descriptive line beneath it, or a card with an eyebrow label over a bold title. In each case, you have two text blocks that read as one unit, but only one clear “heading” in the semantic sense.
Here’s a typical card example:

Option 1: Heading level stacking
The most obvious markup might be an <h3> above an <h2>, because the smaller text “should” map to a lower heading level. That’s usually the weakest choice.
<h3>Subheading</h3>
<h2>Heading</h2>
Styling a heading level based on visual size couples presentation to document structure. If you need a class to fix the look, then the heading level was doing styling work, not semantic work. And using two headings for one conceptual unit clutters the outline with a heading that has no matching content section.
A somewhat better variation flips the levels: the smaller line becomes the <h2>, and the larger display text becomes an <h3>. That at least keeps the document outline closer to the reading order, since the higher-level heading comes first.
<h2 class="card-subheading">Subheading</h2>
<h3 class="card-heading">Heading</h3>
Still, this only works if the small line actually functions as the more significant heading in context. In most card and subsection designs, it does not.
Option 2: One heading, one plain block
A more honest approach is to decide which line is the real heading. Typically that’s the larger, more specific text, with the smaller line acting as supplementary context.
<div class="card-subheading">Subheading</div>
<h3 class="card-heading">Heading</h3>
This keeps the outline clean. The trade-off is that a screen reader user who jumps from heading to heading will land on the heading and then hear the supporting line only if they keep reading. If the supporting line carries meaning on its own, that’s a minor loss. Reversing the visual order via CSS so the real heading appears first in the DOM is tempting but creates disorientation for sighted screen reader users who see one order and hear another.
Option 3: One heading element, inline structure
Since both lines describe a single thing, the cleanest solution often is to put them inside one heading element. The supporting line becomes <strong>, which provides a styling hook without inventing a new heading level.
<h2>
<strong>Subheading</strong>
Heading
</h2>
The CSS can then target the <strong> directly:
h2 strong {
display: block;
font-size: 75%;
opacity: 0.75;
}
For this to work, the two pieces have to read naturally as a single phrase. If they don’t, a colon or other separator can bridge them:
<h2>
<strong>New Podcast:</strong>
Struggling with Basic HTML
</h2>
ARIA subtitle role
There’s also a dedicated ARIA role for this exact pattern:
https://twitter.com/stevefaulkner/status/1236241209686966272
Applied to the subheading line, it looks like this:
<h2 class="card-heading">Heading</h2>
<div role="doc-subtitle">Subheading</div>
Basing styles on the role itself keeps the styling connected to the semantics:
[role="doc-subtitle"] { }
Browser testing suggests the role="doc-subtitle" is generally exposed as a heading without a level. JAWS is the odd one out, treating it like an <h2>. That inconsistency is workable, and some accessibility experts consider putting the subheading first acceptable. But support is still not uniform enough to lean on entirely.
What to avoid
One tempting anti-pattern is to use an <hgroup> with the subheading as a sibling paragraph. The role is to label the heading, not act as a separate heading level, and the grouping semantics don’t hold up outside certain experimental implementations.
Another is using the ::before or ::after pseudo-elements to inject the subheading text. Screen reader support for generated content has improved, but it’s not universal. The text also can’t be selected or found with the browser’s in-page search, which makes it a poor vehicle for real content.
The short version: pick the single element that is actually the heading, keep any label line nestled inside it if it reads as one unit, and reach for role="doc-subtitle" when the components need to stay distinct.



