0Support

Document semantics selectors

Explore how language, direction, definition, and headings affect real selector matches.

Read the guide

Ask AI

Document semantics selectors

Response generated with openai/gpt-5.4-nano. AI can make mistakes. Always review the result.
Current selector:lang(fr)
Support unknownSpecificity—

Preparing the scenario and its results…

:lang() matches the effective language, including inheritance and regional variants.

Selector preview

Bonjour, texte français hérité.

Hello, explicit English text.

Salut, français canadien.

HTML
<section class="semantic-demo language-demo" lang="fr" aria-label="Language example">
  <p data-semantic-target="inherited-french">Bonjour, texte français hérité.</p>
  <p lang="en" data-semantic-target="explicit-english">Hello, explicit English text.</p>
  <p lang="fr-CA" data-semantic-target="regional-french">Salut, français canadien.</p>
</section>
CSS
:lang(fr) {
  outline: 3px solid #f97316;
  outline-offset: 3px;
  background-color: color-mix(in oklch, #f97316 16%, transparent);
}
JS
// This scenario needs no JavaScript.

Educational guide to document semantics selectors

Select what the document means

Compare language, direction, definition, and heading levels without confusing attributes, styles, and computed semantics.

Back to playground

Mental model

These pseudo-classes query information that the browser obtains from the document; the visible attribute does not always tell the whole story.

:lang() · :dir()

Effective values

:lang() can match through inheritance or a regional variant. :dir() queries inherited semantic direction or the direction calculated by dir="auto", not the CSS direction property.

:defined

A state that really changes

Built-in HTML elements are already defined. A custom element becomes :defined when registered; that name cannot be unregistered, so restart uses another one.

:heading · :heading()

Semantic heading

:heading selects elements with a heading level; :heading() filters integer levels, including several separated by commas. Support is still experimental.

headingoffset · headingreset · aria-level

HTML level, tag, and ARIA

HTML can offset or reset the level with headingoffset and headingreset, so the level can differ from h1–h6. aria-level expresses accessibility semantics but does not by itself make a div match these selectors. Safari 27.2 still selected an offset h1 as level 1; the demo uses native headings without offsets.

Language and direction

Compare the effective semantic value with the attributes written on each element.

:lang()

:lang(<language-range>)

Elements whose effective language matches the specified range.

Specificity(0, 1, 0)
Behavior
  • Language can be inherited from an ancestor even when the element has no lang attribute.
  • :lang(fr) also includes regional variants such as fr-CA; an explicit lang="en" interrupts that inheritance.
Examples
SelectorSpecificity(0, 1, 0)
:lang(fr)
HTML
Matches: <section lang="fr">
Matches:   <p>Bonjour</p>
  <p lang="en">Hello</p>
</section>
SelectorSpecificity(0, 1, 0)
:lang(fr)
HTML
Matches: <div lang="fr-CA">
Matches:   <p>Salut</p>
</div>
Common mistakes
  • It is not equivalent to [lang="fr"], which compares only an attribute on the element itself.
  • Visible text does not determine language; the language assigned to the element does.
Read the complete definitionOpens in a new tab

:dir()

:dir(ltr | rtl)

Elements with the requested effective semantic direction.

Specificity(0, 1, 0)
Behavior
  • A descendant can inherit rtl without having dir="rtl" on its own tag.
  • dir="auto" calculates direction from content; the Arabic example text gives rtl.
Examples
SelectorSpecificity(0, 1, 0)
:dir(rtl)
HTML
Matches: <div dir="rtl">
Matches:   <p>مرحبا</p>
  <p dir="ltr">Hello</p>
</div>
SelectorSpecificity(0, 1, 0)
:dir(rtl)
HTML
<div dir="ltr">
Matches:   <p dir="auto">مرحبا</p>
  <p dir="auto">Hello</p>
</div>
Common mistakes
  • It is not equivalent to [dir="rtl"], which finds only explicit attributes.
  • The CSS direction property changes presentation but not the result of :dir().
Read the complete definitionOpens in a new tab

Element definition

Observe the difference between built-in HTML and a custom element before and after registration.

:defined

Built-in elements and custom elements that already have a registered definition.

Specificity(0, 1, 0)
Behavior
  • A built-in HTML button matches from the start; an autonomous custom element starts undefined.
  • After customElements.define(), the same node starts matching. The second example represents that later moment.
Examples
SelectorSpecificity(0, 1, 1)
button:defined
HTML
<div>
Matches:   <button type="button">Built-in button</button>
  <demo-card>Waiting</demo-card>
</div>
SelectorSpecificity(0, 1, 1)
demo-card:defined
HTML
<!-- after customElements.define("demo-card", ...) -->
Matches: <demo-card>Defined</demo-card>
Common mistakes
  • It does not mean the element is valid, visible, or finished loading its content.
  • customElements.define() cannot be undone for the same name: the demo restarts with a new one.
Read the complete definitionOpens in a new tab

Headings and levels

Examples show the intent of Selectors Level 5; the playground counts matches only when this browser produces them natively.

:heading

Elements with a heading level according to the document language’s semantics.

Specificity(0, 1, 0)
Behavior
  • The form without parentheses covers all heading levels and contributes (0, 1, 0) specificity.
  • A div with role="heading" and aria-level did not pass observed native matching; the demo uses h1–h3.
Examples
SelectorSpecificity(0, 1, 0)
:heading
HTML
<section>
Matches:   <h1>Main heading</h1>
Matches:   <h2>Section heading</h2>
  <p>Content</p>
</section>
SelectorSpecificity(0, 1, 0)
:heading
HTML
<section>
  <div role="heading" aria-level="2">Text with an ARIA heading role</div>
Matches:   <h3>Subsection heading</h3>
</section>
Common mistakes
  • Do not assume an ARIA role makes an arbitrary element match :heading.
  • Do not mistake parseable syntax for CSS and DOM matching support.
Support and compatibility

September 25, 2026 test: Safari 27.2 matched; Firefox 156, even with the preference enabled, and Chrome 153 rejected this form. The playground requires a positive native match.

Read the complete definitionOpens in a new tab

:heading()

:heading(<integer>#)

Headings whose semantic level belongs to the specified list of integers.

Specificity(0, 1, 0)
Behavior
  • :heading(2) filters level 2; :heading(1, 2) accepts several levels in one functional form.
  • The pseudo-class contributes (0, 1, 0); adding a type such as h2 also contributes (0, 0, 1).
Examples
SelectorSpecificity(0, 1, 0)
:heading(2)
HTML
<section>
  <h1>Main heading</h1>
Matches:   <h2>Section heading</h2>
  <h3>Subsection heading</h3>
</section>
SelectorSpecificity(0, 1, 0)
:heading(1, 2)
HTML
<article>
Matches:   <h1>Main heading</h1>
Matches:   <h2>Section heading</h2>
  <h3>Subsection heading</h3>
</article>
Common mistakes
  • An empty list or names such as :heading(foo) are invalid: integers are required.
  • HTML defines offset levels with headingoffset, but Safari 27.2 did not apply them to observed matching. The demo does not simulate this case.
Support and compatibility

September 25, 2026 test: Safari 27.2 matched :heading(2); Firefox 156 with the preference and Chrome 153 rejected it. The playground probes each chosen level with a native heading.

Read the complete definitionOpens in a new tab