Introduction to :heading()
The :heading() functional pseudo-class represents all heading elements whose levels match a comma-separated list of integers, enabling developers to style elements at specific heading levels without writing repetitive selector lists.
Why :heading() Matters for Modern CSS
This selector eliminates the need for verbose multi-selector groups like h1, h2, h3, h4, h5, h6, replacing them with a clean, declarative approach that directly expresses intent. Rather than listing every heading element individually, you specify which levels you want to target, making your CSS more readable and maintainable. The functional pseudo-class aligns with the CSS specification's evolution toward more intuitive, semantic selectors that match how developers think about document structure. This becomes particularly valuable in component-based architectures where consistent heading hierarchy matters for both accessibility and visual design, and where reducing selector complexity helps prevent specificity wars in large stylesheets.
Understanding how :heading() relates to other CSS functions helps build a stronger foundation. The Functions in CSS guide explores the broader category of functional pseudo-classes and how they can simplify your stylesheets.
- Eliminates the need for verbose selectors like
h1, h2, h3, h4, h5, h6 - Provides a declarative, semantic way to target heading elements
- Aligns with the CSS specification's evolution toward more intuitive selectors
- Complements modern component-based architecture where heading hierarchy matters
Visual suggestion: Side-by-side comparison of traditional selectors vs. :heading() syntax
Syntax and Parameters
Understanding the Syntax
The :heading() function accepts a comma-separated list of integers representing heading levels (1-6):
:heading(1, 3, 5) {
color: tomato;
}
Parameters Explained
The <integer> parameter uses a one-based indexing system where each number directly corresponds to an HTML heading element. A value of 1 matches <h1> elements, 2 matches <h2> elements, and this mapping continues through 6 for <h6> elements. The function accepts multiple values as a comma-separated list, allowing you to target several heading levels simultaneously without repetition. There should be no spaces between values in the list--:heading(1,3,5) is valid while :heading(1, 3, 5) would be parsed incorrectly. This parameter structure mirrors other functional pseudo-classes in CSS Selectors Level 5, providing consistency across the specification. The one-based indexing aligns with how heading levels are commonly referenced in HTML documentation and developer communication, reducing cognitive overhead when writing styles.
For projects requiring consistent form styling, the :heading() selector pairs well with advanced custom radio and checkbox techniques that leverage modern CSS pseudo-classes for enhanced user interfaces.
- Each integer corresponds to an HTML heading level (h1 through h6)
- Values are comma-separated to target multiple levels
- No spaces between values in the list
- Range is 1-6 corresponding to H1-H6 elements
Visual suggestion: Diagram showing heading level hierarchy (H1 > H2 > H3...)
The :heading Pseudo-Class
Simple vs. Functional Forms
While :heading() allows selective targeting, :heading matches ALL heading elements:
:heading {
color: tomato;
font-style: italic;
}
When to Use Each Form
Choose the simple :heading pseudo-class when you want to apply uniform styling across all heading levels, such as setting base typography, establishing font families, or defining default colors that should cascade consistently. Use the functional :heading() form when you need granular control--perhaps styling primary headings larger and bolder while keeping nested section headings more subdued. The functional form excels in responsive design scenarios where different heading levels may require different treatments at various breakpoints, or when working with design systems that distinguish between page titles, section headers, and subsection headings. A common pattern is to use :heading for foundational styles and :heading() overrides for specific hierarchy levels, creating a clear separation between base and variant styling.
| Use Case | Selector | Example |
|---|---|---|
| Style all headings uniformly | :heading | Setting base heading colors |
| Target specific levels | :heading(1, 2) | Primary and secondary headings only |
| Odd/even levels | :heading(1, 3, 5) | Styling odd vs. even headings |
Visual suggestion: Decision tree for choosing between :heading and :heading()
Specificity and Cascade Behavior
Understanding Specificity
The :heading() functional pseudo-class has the same specificity as a class selector: 0-1-0, as defined in the CSS Selectors Level 5 Specification. This means it carries more weight than a single element selector like h1 (which has specificity 0-0-1) but less than an ID selector. Understanding this is crucial when building maintainable CSS architectures because it affects how your styles will interact with existing codebases. When combining :heading with other selectors, the specificity adds up normally--a descendant selector like article :heading has specificity 0-2-0 (one class from :heading plus one element from article). This has practical implications: if your project uses utility classes or component classes with :heading, the combined specificity will determine which styles win in the cascade. Planning your specificity strategy prevents the common pitfall of needing increasingly specific selectors to override styles, which leads to bloated, hard-to-maintain stylesheets.
- Compare specificity:
:heading(0-1-0) vs.h1(0-0-1) - Practical implications when combining with other selectors
- How specificity affects override behavior in cascade
Specificity Examples
/* Specificity: 0-1-0 */
:heading {
color: tomato;
}
/* Specificity: 0-1-1 (higher) */
section:heading {
color: slateblue;
}
/* Specificity: 0-2-0 (highest in this set) */
article section:heading {
color: green;
}
Visual suggestion: Specificity comparison table with examples
Practical Applications
Consistent Heading Styling
Apply base styles to all headings efficiently:
:heading {
font-family: system-ui, -apple-system, sans-serif;
font-weight: 600;
line-height: 1.2;
}
Using :heading for base typography eliminates the repetition of listing all six heading elements, reducing the chance of inconsistencies where one level gets forgotten. It ensures that every heading on your site inherits the same fundamental typography, creating visual harmony across different sections and page types. This approach is particularly valuable in large-scale web applications where multiple teams might add content, and a single source of truth for heading styles prevents fragmentation. The selector also future-proofs your code slightly--if HTML expands to include heading levels beyond h6 (unlikely but theoretically possible), :heading would automatically include them.
For developers working on JavaScript-heavy applications, understanding how heading selectors interact with dynamic content updates is essential. Our guide on JavaScript Closest Method provides complementary techniques for traversing and manipulating DOM elements alongside CSS styling.
- Setting typography defaults for all headings
- Ensuring visual consistency across different heading levels
- Reducing CSS repetition in stylesheets
Targeted Heading Styling
Style specific heading levels differently:
/* Primary headings - larger and bold */
:heading(1) {
font-size: 2.5rem;
margin-bottom: 1rem;
}
/* Secondary headings */
:heading(2) {
font-size: 2rem;
margin-bottom: 0.75rem;
}
/* Tertiary and below */
:heading(3, 4, 5, 6) {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
Section-Based Heading Styling
Scope heading styles within specific containers:
article :heading(1) {
color: #1a1a1a;
}
aside :heading(1) {
color: #4a4a4a;
font-size: 1.75rem;
}
Visual suggestion: Code example showing rendered output with different heading styles
Accessibility Considerations
Semantic Matching Only
The :heading() pseudo-class matches only elements that are semantically recognized as headings (h1-h6), as documented on MDN Web Docs. It does NOT match elements using role="heading" or aria-level attributes, which are used in ARIA to create programmatic headings for non-heading elements. This distinction matters for accessible web development because accessibility tools like screen readers navigate by semantic HTML, not CSS selectors. If you're using ARIA attributes to create virtual heading structures (common in single-page applications or complex widgets), you'll need [role="heading"] attribute selectors instead. The CSS selector also cannot style pseudo-elements that serve as visual headings--stick to semantic HTML for the best compatibility with assistive technologies.
For teams prioritizing inclusive design, our accessibility services provide comprehensive auditing and implementation support for WCAG compliance across your digital properties.
- For ARIA headings, use
[role="heading"]attribute selector instead - Accessibility tools rely on semantic HTML, not CSS selectors
- Always ensure proper heading hierarchy in HTML structure
Screen Reader Behavior
Screen readers navigate by heading level regardless of CSS styling--the accessibility tree is determined by HTML structure, not visual presentation. This means styling an h1 to look small with CSS won't confuse screen readers, but it may visually mislead sighted users about content importance. Maintain a logical heading order where H1 represents the page title, H2 marks major sections, and H3 and below indicate subsections, without skipping levels (never jump from H2 to H4, for example). This hierarchical structure helps all users scan content efficiently and is fundamental to WCAG compliance. Even when using :heading() for flexible visual styling, preserve the underlying semantic structure that makes content navigable.
Browser Support and Compatibility
Current Support Status
As of early 2026, :heading() and :heading have limited browser availability and are marked as experimental, as noted in the CSS-Tricks coverage of headings. The selectors are not yet part of Baseline compatibility, meaning they shouldn't be relied upon for production styles without fallbacks. This experimental status has implications for cross-browser testing strategies--you'll need to check compatibility tables before deployment and consider whether the styling enhancement is critical enough to require workarounds. For most production sites, traditional selectors like h1, h2, h3 remain the safe choice, with :heading() reserved for progressive enhancement in supported browsers. The feature may require CSS feature queries (@supports) to conditionally apply enhanced styles only where supported.
Our quality assurance services include comprehensive cross-browser testing to ensure your implementations work reliably across different browsers and devices.
- Not yet part of Baseline compatibility
- Requires checking browser compatibility tables before production use
- Feature may require CSS feature queries (
@supports) for progressive enhancement
Progressive Enhancement Strategy
/* Base styles for all browsers */
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
}
/* Enhanced styles where supported */
@supports (selector(:heading)) {
:heading {
font-weight: 600;
}
}
Visual suggestion: Browser compatibility table showing support status
Best Practices
1. Use for Consistent Base Styling
Apply base typography to all headings cleanly:
:heading {
font-family: inherit;
font-weight: 700;
color: inherit;
}
2. Combine with Scoping Selectors
Scope heading styles contextually:
.main-content :heading(1, 2) {
color: #333;
}
.sidebar :heading {
font-size: 1.25rem;
}
3. Maintain Heading Hierarchy
Even with styling flexibility, preserve semantic structure:
/* Styling doesn't replace proper HTML hierarchy */
:heading(1) {
font-size: 2.5rem;
}
/* Not: skipping H2 and jumping to H3 */
4. Consider Fallback Strategies
Provide traditional selectors alongside new pseudo-classes during transition:
/* Traditional selector (works everywhere) */
h1, h2, h3, h4, h5, h6 {
color: tomato;
}
/* Modern approach where supported */
:heading {
color: tomato;
}
Common Use Cases Summary
| Use Case | Selector Pattern | Purpose |
|---|---|---|
| All headings | :heading | Base typography, reset styles |
| Primary levels | :heading(1, 2) | Main page hierarchy |
| Section headers | :heading(3, 4) | Subsection styling |
| Meta content | :heading(5, 6) | Fine print, notes |
| Conditional | :heading(1, 3, 5) | Alternate styling pattern |
Conclusion
The :heading() functional pseudo-class and :heading pseudo-class represent a significant improvement in how developers can style heading elements. While browser support remains limited, these selectors offer cleaner, more maintainable CSS for projects where support is available or where progressive enhancement strategies are employed. By reducing selector verbosity and providing semantic targeting, :heading() aligns with modern CSS principles that prioritize readability and intention-revealing code.
Key Takeaways
- Use
:heading()for selective heading level targeting - Use
:headingfor universal heading styling - Understand specificity implications (0-1-0)
- Maintain semantic HTML alongside visual styling
- Plan for graceful degradation in unsupported browsers