DEV Community

orjinameh
orjinameh

Posted on

How I Fixed PageSpeed Insights Accessibility Issues in a Next.js App

Recently, I worked on fixing accessibility issues flagged by PageSpeed Insights across 13 pages of a production Next.js application called Accuguide — a platform that helps people discover accessible places and services.

In this article, I'll walk through the exact issues I found and how I fixed them. These are common mistakes that show up in many Next.js apps, so hopefully this saves you some debugging time.


The Problems PageSpeed Found

Running PageSpeed Insights on each page revealed 5 main categories of issues:

  1. Low contrast text
  2. Links relying on color alone to be distinguishable
  3. Invalid <dl> element structure
  4. Missing <main> landmark
  5. Heading elements skipping levels

Let's go through each one.


1. Low Contrast Text

PageSpeed flagged several elements with insufficient contrast between text and background colors.

The culprit: A global .secondary-text CSS class using text-slate-500 which doesn't meet WCAG AA contrast requirements on a light background.

/* ❌ Before */
.secondary-text {
  @apply text-slate-500 dark:text-slate-400;
}
Enter fullscreen mode Exit fullscreen mode
/* ✅ After */
.secondary-text {
  @apply text-slate-700 dark:text-slate-300;
}
Enter fullscreen mode Exit fullscreen mode

The same issue appeared in footer navigation links which were using text-slate-500:

/* ❌ Before */
className="py-2 font-semibold text-slate-500 text-sm"

/* ✅ After */
className="py-2 font-semibold text-slate-700 text-sm dark:text-slate-300"
Enter fullscreen mode Exit fullscreen mode

Also fixed amber and green status messages in a location component:

/* ❌ Before */
Location access denied.
Showing results near you

/* ✅ After */
Location access denied.
Showing results near you
Enter fullscreen mode Exit fullscreen mode

Rule of thumb: Always check contrast ratios using tools like WebAIM Contrast Checker. Aim for at least 4.5:1 for normal text.


2. Links Relying on Color Alone

PageSpeed flagged a "Netlify" link in the footer that was only distinguishable by its blue color — no underline, no other visual indicator.

/* ❌ Before */

  Netlify

Enter fullscreen mode Exit fullscreen mode
/* ✅ After */

  Netlify

Enter fullscreen mode Exit fullscreen mode

I also added a global underline to all anchor tags in the global CSS to prevent this across the entire app:

a {
  @apply text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300 underline;
}
Enter fullscreen mode Exit fullscreen mode

Why it matters: Users with color blindness or low vision cannot distinguish links from regular text if color is the only differentiator.


3. Invalid <dl> Element Structure

PageSpeed flagged this error:

<dl>'s do not contain only properly-ordered <dt> and <dd> groups

The app had a stats section using a <dl> element but the StatCard component inside it was using <div> and <p> tags instead of proper <dt> and <dd> tags.

/* ❌ Before */
export default function StatCard({ name, value, icon: Icon, color }) {
  return (





      {name}

  )
}
Enter fullscreen mode Exit fullscreen mode
/* ✅ After */
export default function StatCard({ name, value, icon: Icon, color }) {
  return (





      {name}

  )
}
Enter fullscreen mode Exit fullscreen mode

Two things changed:

  • <div><dd> for the value
  • <p><dt> for the label
  • Added aria-hidden="true" to the decorative icon

4. Missing <main> Landmark

PageSpeed flagged:

Document does not have a main landmark

Screen readers use landmark elements like <main>, <nav>, and <footer> to help users navigate pages efficiently. The app was missing a <main> wrapper around page content.

The fix was simple — add <main> in the root layout:

/* ❌ Before */

  {children}


/* ✅ After */


    {children}


Enter fullscreen mode Exit fullscreen mode

One change, fixes every single page in the app at once.


5. Heading Elements Skipping Levels

PageSpeed flagged that footer section headings were <h3> elements appearing before any <h2> on several pages — skipping a heading level.

/* ❌ Before */
function FooterLinkList({ title, items }) {
  return (

      {title}
      ...

  )
}
Enter fullscreen mode Exit fullscreen mode
/* ✅ After */
function FooterLinkList({ title, items }) {
  return (

      {title}
      ...

  )
}
Enter fullscreen mode Exit fullscreen mode

Why it matters: Screen readers use heading levels to build a table of contents for the page. Skipping levels (h1 → h3) breaks the logical structure and confuses users.


Results

After these fixes, the accessibility score on PageSpeed Insights improved significantly across all 13 pages. The fixes were:

  • Globally applied (footer, layout, global CSS) so they fixed every page at once
  • Minimal code changes with maximum impact
  • All following WCAG AA guidelines

Key Takeaways

  1. Run PageSpeed on every page — issues can vary page by page
  2. Fix global components first — footer, header, and layout fixes apply everywhere
  3. text-slate-500 is usually too light — bump to text-slate-700 for body text
  4. Always add a <main> landmark in your root layout
  5. Use <dt> and <dd> correctly inside <dl> elements
  6. Don't rely on color alone to distinguish links — always add underline

If you found this helpful, check out the Accuguide repo and my GitHub profile.

Have questions or spotted something I missed? Drop a comment below!

Top comments (0)