CSS display: contents and Screen Reader Accessibility

The CSS display: contents property allows developers to remove a container element's visual box from the layout tree while keeping its child elements intact, which is especially useful for CSS Grid and Flexbox layouts. However, using this property frequently causes the element—and sometimes its children—to be dropped from the browser's accessibility tree. This article explains how display: contents works, why it creates severe accessibility pitfalls for screen reader users, and best practices for avoiding layout-induced usability barriers.

What is display: contents?

Under standard CSS box model rules, every HTML element generates a rectangular box that influences page layout, borders, backgrounds, and margins. When you assign display: contents to an element, the browser essentially ignores that container’s visual box.

The element itself ceases to generate a render box, meaning its backgrounds, borders, padding, and margins are not rendered. Instead, its children are treated as if they were direct children of the element's parent.

This property was primarily introduced to simplify layout implementations in CSS Grid and Flexbox. Developers often need wrapper elements in the HTML for markup structure, state management, or component boundaries, but these extra wrappers can disrupt the immediate parent-child relationship required for grid tracks or flex alignments. display: contents solves the layout issue without requiring developers to alter the HTML source structure.

The Screen Reader Accessibility Problem

While the CSS specification states that display: contents should only affect visual box generation and not the semantic structure of a document, real-world browser implementations have struggled to keep visual layout separate from accessibility APIs.

Browsers construct an "accessibility tree" alongside the DOM and the render tree. Assistive technologies, including screen readers like NVDA, JAWS, and VoiceOver, rely entirely on this accessibility tree to convey context, element roles, and structural relationships to users.

Historically, many major browser rendering engines tightly coupled their accessibility tree generation to the layout engine. Consequently, when an element’s visual box was destroyed via display: contents, the browser erroneously stripped the element’s semantic meaning and role from the accessibility tree as well.

Common Breakages Caused by display: contents

  1. Broken Lists: Applying display: contents to <ul> or <ol> elements often causes screen readers to lose list semantics. Instead of announcing "list, 4 items," the screen reader treats the inner <li> tags as plain text blocks, robbing users of vital navigation landmarks and item counting.

  2. Destroyed Table Structure: Data tables are among the most fragile elements on the web. Applying display: contents to <table>, <tr>, <thead>, <tbody>, or <td> elements dismantles the tabular data model. Screen readers will no longer announce row/column numbers or header associations, rendering the data incomprehensible to non-sighted users.

  3. Loss of Interactive Roles and Focus: When applied to interactive elements—such as <button>, <fieldset>, or custom components using ARIA attributes—the browser may cease to expose the element's accessible name, role, and states. In severe cases, keyboard focus rings may become misaligned, invisible, or untargetable.

  4. Headings and Landmarks: Applying the property to semantic wrappers such as <nav>, <main>, <aside>, or heading tags (<h1>–<h6>) can strip landmark navigation shortcuts, isolating assistive technology users from essential structural orientation.

Current Browser Status and Best Practices

Browser vendors have implemented multiple fixes over recent years to decouple accessibility semantics from the layout engine. Modern versions of Chromium, Firefox, and Safari have resolved several of the most egregious bugs regarding list and generic element semantics. However, inconsistencies remain across platforms, particularly concerning tables, ARIA landmarks, and focus management.

To maintain accessibility:

  • Never apply display: contents to semantic HTML elements: Avoid using it on <table>, <ul>, <ol>, <button>, <select>, <nav>, <main>, <header>, or <footer>.
  • Restrict usage to non-semantic containers: Only use display: contents on neutral tags like <div> or <span> that serve purely as visual styling wrappers and contain no inherent semantic roles or ARIA designations.
  • Test with real assistive technology: If you must use display: contents, verify the page in multiple screen readers (VoiceOver, NVDA, and Orca) to ensure the accessibility tree remains fully populated.