Table of Contents
- Introduction
- aria-hidden for Decorative Icons
- Solution
- Screen Reader Notifications for External Links
- Solution
- Ensuring Contrast
- Common Problem
- Solution
- focus-visible Styles
- Implementation with UnoCSS
- Elements Often Overlooked
- Underlines on Inline Links
- Solution
- Form Accessibility
- Inline Validation
- Required Field Markers
- role Attribute on figure Elements
- role Attribute on List Elements
- Other Improvements
- width/height Attributes on Images
- aria-live on Hero Slider
- aria-labelledby on dialog
- aria-current on Pagination
- Copy Button aria-label Update
- Summary
- Part of a Series
Introduction
“Accessibility” might seem like something easy to put off. But when you actually work on it, you realize that improving contrast, keyboard navigation, and focus indicators directly enhances usability for every user.
This article introduces the improvements made to achieve a PageSpeed Accessibility score of 100 on an Astro + UnoCSS site, organized by category.
aria-hidden for Decorative Icons
UnoCSS Iconify icons (i-lucide-*) are often used as visual decoration, but when screen readers read them aloud, they announce “image” or “unknown image,” which causes confusion.
Solution
Add aria-hidden="true" to decorative icons.
<span class="i-lucide-mail" aria-hidden="true"></span>
Contact
This was applied to over 30 icons across the site. Be careful not to miss icons inside components like StatBar, Callout, ServiceCard, and ProcessFigure.
Screen Reader Notifications for External Links
External links opened with target="_blank" visually indicate they open in a new tab, but this isn’t communicated to screen reader users.
Solution
Add visually hidden supplementary text to external links.
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
Example
<span class="sr-only">(opens in a new tab)</span>
</a>
Using the rehype-external-links plugin, target="_blank" and rel can be automatically added to external links in Markdown. The SR notification text is added on the template side.
Ensuring Contrast
Insufficient contrast is the most common issue flagged by PageSpeed Insights.
Common Problem
Using text-slate-400 from UnoCSS’s color palette results in a contrast ratio of about 3:1 against a white background, failing the WCAG AA requirement of 4.5:1.
Solution
Changing text-slate-400 → text-slate-500 (contrast ratio 4.6:1) clears the requirement. This is commonly used for supplementary text like dates and captions, so check across the entire site.
focus-visible Styles
For users who navigate sites with a keyboard, focus indicators are the only way to know “where I am now.” WCAG 2.4.7 requires focus visibility.
Implementation with UnoCSS
Set common focus styles for buttons and links. Using UnoCSS’s shortcut feature, you can define it in one place and apply it everywhere.
shortcuts: {
'ac-btn': '... focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:outline-none',
}
focus-visible is a pseudo-class that shows the ring only during keyboard navigation, not on mouse clicks. It provides better UX than focus, so use this one.
Elements Often Overlooked
- Copy buttons
- Scroll-to-top button
- Anchor ad close button
- Modal close buttons
Underlines on Inline Links
PageSpeed may flag “Links are identifiable only by color.” This is a problem for users with color vision deficiencies who cannot distinguish links.
Solution
Make underlines always visible instead of only on hover. Using UnoCSS shortcuts for consistency is recommended.
shortcuts: {
'ac-link': 'underline decoration-brand-300 underline-offset-2 hover:decoration-brand-500 transition-colors',
}
Form Accessibility
Accessibility is especially important where users provide input, such as contact forms.
Inline Validation
Display error messages immediately on blur/input events, coordinating with the following aria attributes:
aria-invalid="true"— Notifies that the input is invalidaria-describedby— References the error message’s ID
<input
type="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<p id="email-error" role="alert">Please enter a valid email address</p>
Required Field Markers
A visual * mark alone is insufficient. Add supplementary text for screen readers.
<span aria-hidden="true">*</span>
<span class="sr-only">(required)</span>
role Attribute on figure Elements
Setting role="img" on <figure> elements hides child elements from screen readers. For components containing icons and descriptive text (InsightGrid, ProcessFigure, Timeline), change to role="group" to keep internal content accessible.
role Attribute on List Elements
When CSS list-style: none is applied, Safari’s screen reader (VoiceOver) has a known bug where it no longer recognizes the element as a list.
Add role="list" to <ol> / <ul> elements in breadcrumbs, sidebars, and footers. Check all lists with customized appearance.
Other Improvements
width/height Attributes on Images
Images without explicit width and height cause layout shifts (CLS — Cumulative Layout Shift) when loading completes. Specify sizes for all images, including avatars (32×32, 48×48, 64×64px) and YouTube thumbnails (480×360px).
aria-live on Hero Slider
Auto-rotating sliders don’t communicate changes to screen reader users. Prepare an aria-live="polite" region and notify with text like “Slide 1 / 4: [title].“
aria-labelledby on dialog
Reference the title element’s ID with aria-labelledby on <dialog> elements so screen readers can announce the modal’s purpose.
aria-current on Pagination
Set aria-current="page" on the current page number to notify screen readers that it is “the current page.”
Copy Button aria-label Update
When clipboard copy succeeds, dynamically update aria-label to “Copied” to notify screen readers of the state change.
Summary
Accessibility improvements are small individual changes, but together they significantly improve overall site quality. The three most impactful changes were:
- Applying focus-visible globally: Dramatically improved keyboard navigation
- Fixing contrast ratios: Simply changing
text-slate-400→text-slate-500cleared WCAG AA - SR notifications for external links: Combined with
rehype-external-linksfor automated coverage of all links
Start by scanning your site with axe DevTools and tackling the automatically detectable issues first.
Part of a Series
This article is part of the “Astro Site Quality Improvement Guide” series. Separate articles cover performance, SEO, and UX improvements as well.
Accessibility Improvement Workflow
Automated Testing
Use axe DevTools and Lighthouse to identify machine-detectable issues.
Manual Testing
Try navigating with keyboard and screen reader yourself.
Fix
Add aria attributes, fix contrast, and add focus styles.
Re-test
Confirm a score of 100 on PageSpeed Accessibility.
- Done: Text contrast ratio is 4.5:1 or higher (3:1 for large text)
- Done: All interactive elements have focus-visible styles
- Done: Decorative icons have aria-hidden="true"
- Done: External links have screen reader notifications
- Done: Forms have inline validation with aria-invalid integration
- Done: Images have width/height attributes (CLS prevention)
- Done: List elements have role="list" (list-style:none workaround)
What's the difference between axe DevTools and Lighthouse?
Should aria attributes be added to every element?
Does a PageSpeed Accessibility score of 100 mean WCAG compliance?
Gui
CEO of Acecore. A versatile engineer covering system development, web production, infrastructure operations, and IT education. Enjoys solving organizational and human challenges through technology.
Want to learn more about our services?
We provide comprehensive support including system development, web design, graphic design, and IT education.