How CSS Can Affect Semantic HTML

by Matt Cotton
5 min read

Perhaps understandably, most developers believe that CSS does not, or perhaps cannot, affect the semantic meaning of the markup. This is backed up by a lot of responses on stack overflow questions related to this topic. Unfortunately this is not true.

There are a number of ways in which the semantic meaning can be affected by CSS, whether intentional or a bug. Below I will explain a number of these ways and what can be done to mitigate this. 

When is a list not a list?

It is very common to hide bullet points on ul's using list-style: none;. This could be when adding a bespoke bullet style as a ::before pseudo element or for a list when bullets are not required by the design. Usually though there is a desire to keep the semantic meaning of the list. In most browsers this is not a problem and the example below will keep the semantic list meaning - resulting in a screen reader still announcing this a “list of 4 items”.

See the Pen List without defined role attribute by Matthew Cotton (@mcotton24) on CodePen.

The problem comes when using Safari in conjunction with Mac’s free built-in screen reader VoiceOver. In this case the semantic meaning is lost and the example above will be announced as standalone text elements. This is not a very well known phenomenon and you may think that it is a bug with either Safari or VoiceOver but it is not. As explained in Scott O’Hara’s article “Fixing” Lists this is an intentional decision made by Apple’s developers. There is a solution to this which is to define the role of the ul element as ”list”. As shown in the example below:

See the Pen List with defined role attribute by Matthew Cotton (@mcotton24) on CodePen.

This is far from an ideal solution as it is applying an ARIA attribute to an element that should already have a semantic meaning. In general we should put more consideration into when to use unordered lists to prevent overuse when they don’t add any benefit to the end user. But when a list is needed and the default bullets have been removed, this fix does add back in the required semantic meaning.

Don’t get too far away from the document source order

The potential of grid and flex are huge and allow for an almost limitless way to style a page but it is important to understand the effects that has on the semantic meaning of the page. For example if you are arranging a grid of cards using grid-area, it should be used with caution. As it could lead to a very unintuitive layout when navigating the page using the keyboard. The example below shows an extreme example of using grid-area to set the order of a set of cards.

See the Pen Untitled by Matthew Cotton (@mcotton24) on CodePen.

There is also the temptation that this can be improved by altering the tabindex of each card to match the order set by the grid-area value. This would require tabindex‘s to be set to values of 1 and above. This will in fact cause more accessibility issues by giving these cards keyboard focus before elements with no tabindex value. Potentially resulting in confusing keyboard navigation of the entire page.

The better solution here would be to sort the data server-side, rather than adjust the order client-side. This ensures that the card positioning matches the order they appear in the DOM.

Using CSS to hide element changes the semantic meaning

I’ve saved probably the most obvious and well-known way that CSS can affect semantic meaning until last. Setting an element to display: none will remove it and its descendants' semantic meaning as they will be ignored by screen readers as well as visually removed from the page - even though the element and its descendants will still exist in the DOM. 

See the Pen Use of display:none by Matthew Cotton (@mcotton24) on CodePen.

Unintended effects of display: contents

What you may not know though is that display: contents can unexpectedly affect the semantic meaning of elements. When support for display: contents was introduced to browsers most implemented it incorrectly when compared with the spec. The spec states:

"The display property has no effect on an element’s semantics: these are defined by the document language and are not affected by CSS." - CSS Working Group - CSS Display

Whereas on Chrome, Firefox and Safari ‘contents’ was affecting the semantic interpretation of the markup. For example if display: contents was added to a <ul> element this would remove the semantic list role from the element.

This issue has been resolved in most browsers over the last couple of years but as reported by Adrian Roselli there are still some bugs to be aware of. Hidde de Vries also explains this in much more detail in his article More accessible markup with display: contents.

Key Takeaways

If you need expert Web Development support from our 2023 Large Dev Agency of the Year-winning team, head over to our Build services offering.

Written by Matt Cotton
Front End Developer

Matt joined our Development team from a Sheffield-based software house in 2021, bringing his passion for UX and building intuitive user interfaces to the role, as well as his experience creating React apps.

He's also our current CSR Officer, taking charge of our strategy to give back to the environment and local communities.