The Blog

When your pricing table isn't a
Web performanceAccessibility

Comparison tables are one of the most common patterns on service and pricing pages. Designers rarely want an actual HTML table, they want custom layouts with rounded corners, icons, and responsive behaviour. So the table becomes a grid of divs that looks right for sighted users but is completely invisible to screen readers.

The tension

Design wants custom. Accessibility needs semantics.

Comparison tables are one of the most common patterns on service and pricing pages. Side-by-side feature lists, plan tiers, CMS comparisons, they help visitors make decisions quickly.

The problem is that designers rarely want an actual HTML `<table>`. They want custom layouts with rounded corners, icons, colour-coded cells, and responsive behaviour that a native table element doesn't give them easily. So the table becomes a grid of `<div>` elements styled to look like a table.

It looks right. It works for sighted users. And it's completely invisible to screen readers.

A screen reader navigating a `<div>`-based layout has no way to associate a cell with its column header. It can't announce "Basic plan — included" because it doesn't know there's a relationship between the checkmark and the plan name. The visual structure exists only visually.

The fix

ARIA roles that restore the semantics

The solution is straightforward: keep the custom layout, but add ARIA roles that tell assistive technology it's a table.

The container gets `role="table"` with an `aria-label` describing what the table compares. Row wrappers get `role="row"`. Header cells get `role="columnheader"`. Data cells get `role="cell"`.

This gives screen readers the same navigational model as a native `<table>`, users can move between cells and hear the column header announced with each value, while letting the design stay completely custom.

A few details that matter: checkmark and dash icons need `aria-label` attributes ("included" and "not included" respectively). Decorative elements like rounded corners or background patterns get `aria-hidden="true"` so they don't clutter the screen reader experience.

The result

One component, two experiences

The comparison table block in our CMS renders a layout that designers are happy with and assistive technology can navigate properly. The visual experience and the accessible experience are both intentional, not one bolted onto the other.

This pattern applies anywhere you're using custom layouts for tabular data: pricing grids, feature comparisons, plan selectors, specification tables. If the content has rows and columns with header relationships, it needs table semantics, whether or not it uses a `<table>` element.

The effort is small. A handful of ARIA attributes on components you're already building. The impact is that a significant portion of your audience can actually use the content you spent time designing.

For service and pricing pages in particular, this is content that directly influences purchase decisions. Making it inaccessible to screen reader users isn't just an accessibility failure, it's a conversion failure.

If the content has rows and columns with header relationships, it needs table semantics, whether or not it uses a

element.

Little Dash

Building a platform that needs to be accessible?

We build accessible components as part of the design system, not as an afterthought audit. ARIA semantics, keyboard navigation, and reduced motion support are built in from day one.