Diagnose and fix Lighthouse audit failures for performance, accessibility, SEO, and best practices. Use this skill when CI reports low Lighthouse scores or when optimizing the site's Core Web Vitals.
Guide fixes for Lighthouse audit issues in this Gatsby portfolio site.
This project enforces these Lighthouse thresholds in CI:
| Category | Target |
|---|---|
| Performance | 90+ |
| Accessibility | 90+ |
| Best Practices | 90+ |
| SEO | 90+ |
Problem: Largest Contentful Paint is slow due to unoptimized images.
Solution: Use Gatsby Image properly:
// For dynamic images from GraphQL
import { GatsbyImage, getImage } from 'gatsby-plugin-image'
const image = getImage(data.file.childImageSharp.gatsbyImageData)
return <GatsbyImage image={image} alt="description" loading="eager" />
// For hero/above-fold images, use loading="eager"
// For below-fold images, use loading="lazy" (default)
Check: Ensure images in content/paintings/images/ are reasonable sizes (under 2MB source).
Problem: Cumulative Layout Shift from images loading without dimensions.
Solution: GatsbyImage handles this automatically. If using regular <img>:
// Always specify width and height
<img src={src} alt="desc" width={800} height={600} />
Problem: CSS or fonts blocking initial render.
Solution:
font-display: swap in CSSProblem: Images without alt attributes.
Solution: Every image must have alt:
// Decorative images
<GatsbyImage image={image} alt="" />
// Meaningful images
<GatsbyImage image={image} alt="Watercolor painting of autumn leaves" />
For paintings, alt text comes from content/paintings/paintings.yaml.
Problem: Text doesn't meet WCAG contrast ratios.
Solution: Check contrast in src/styles/global.css:
/* Minimum contrast ratios */
/* Normal text: 4.5:1 */
/* Large text (18px+ or 14px+ bold): 3:1 */
/* Example fix */
.text {
color: #333; /* Darker than #666 for better contrast */
background: #fff;
}
Note: This site has dark mode support. Ensure contrast ratios are met in both light and dark themes. CSS variables for colors are defined in src/styles/global.css under :root and [data-theme="dark"] selectors.
Problem: Form inputs or buttons without accessible names.
Solution:
// Buttons with icons need aria-label
<button aria-label="Open menu" onClick={onMenuToggle}>
<MenuIcon />
</button>
// Or use visually hidden text
<button onClick={onMenuToggle}>
<MenuIcon />
<span className="visually-hidden">Open menu</span>
</button>
Problem: Interactive elements not keyboard accessible.
Solution:
<button>, <a>, not <div onClick>)Problem: Page lacks meta description.
Solution: Check gatsby-config.js siteMetadata and use Gatsby Head API:
export const Head = () => (
<>
<title>Page Title | lulutracy</title>
<meta name="description" content="Description here" />
</>
)
Problem: Page title missing or generic.
Solution: Each page should export a Head component with unique title.
Problem: Links using JavaScript instead of href.
Solution: Use Gatsby Link properly:
import { Link } from 'gatsby'
// Correct
<Link to="/about">About</Link>
// Incorrect
<span onClick={() => navigate('/about')}>About</span>
Problem: JavaScript errors in console.
Solution: Run make dev and check browser console. Fix any React warnings or errors.
Problem: Mixed content or insecure resources.
Solution: All external resources should use HTTPS. Check for hardcoded HTTP URLs.
Problem: Dependencies with known vulnerabilities.
Solution:
npm audit
npm audit fix
# Build production version
make build
# Serve locally
make serve
# Run Lighthouse CLI (install globally first)
npx lighthouse http://localhost:9000 --view
loading="eager"loading="eager" to above-fold images