feat(a11y): consolidate ARIA announcements on interactive cards (#133)
- Added comprehensive `aria-label` to the parent `<Link>` in `SubpageGridView`. - Hid redundant textual child elements using `aria-hidden="true"`. - Removed redundant `alt` text from decorative thumbnail `Image`. - Removed unused `albumMap` prop in `AlbumGrid`. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,3 +5,7 @@
|
||||
## 2024-05-25 - SVG Icon Accessibility
|
||||
**Learning:** Adding `aria-hidden="true"` to decorative `<svg>` icons within interactive elements (like `<button>` or `<a>`) that already have an `aria-label` is crucial. Otherwise, screen readers may read the raw vector nodes or fall back to confusing announcements in addition to the label. Similarly, when adding text to visual spinners, use `role="alert"` and `aria-live="polite"` on the container while hiding the SVG graphic.
|
||||
**Action:** When adding or modifying interactive elements with icon graphics, always ensure the graphic is explicitly hidden from screen readers using `aria-hidden="true"` if a text alternative (`aria-label`) is provided.
|
||||
|
||||
## 2024-05-26 - Interactive Card ARIA Labels
|
||||
**Learning:** When building interactive card components that contain multiple pieces of text or badges (like titles and photo counts), screen readers may announce them in a fragmented or confusing way.
|
||||
**Action:** Always apply a comprehensive `aria-label` to the parent link/container that encompasses all meaningful visual data (e.g., titles and item counts). Use `aria-hidden="true"` on redundant text or visual child elements, and set `alt=""` on decorative images to consolidate screen reader announcements into a single, accurate interaction point.
|
||||
|
||||
@@ -27,12 +27,10 @@ interface SubpageGridViewProps {
|
||||
|
||||
function AlbumGrid({
|
||||
albums,
|
||||
albumMap,
|
||||
placeholderMap,
|
||||
slug,
|
||||
}: {
|
||||
albums: SubpageAlbum[];
|
||||
albumMap: Map<string, SubpageAlbum>;
|
||||
placeholderMap: Map<string, Placeholder | null>;
|
||||
slug: string;
|
||||
}) {
|
||||
@@ -46,23 +44,28 @@ function AlbumGrid({
|
||||
href={`/${slug}/${album.slug}`}
|
||||
className="subpage-grid__item"
|
||||
style={ph?.dominantColor ? { backgroundColor: ph.dominantColor } : undefined}
|
||||
aria-label={`${album.albumName}, ${album.assetCount} ${album.assetCount === 1 ? 'photo' : 'photos'}`}
|
||||
>
|
||||
{album.albumThumbnailAssetId ? (
|
||||
<Image
|
||||
src={imageUrl(album.albumThumbnailAssetId, 'preview')}
|
||||
alt={album.albumName}
|
||||
alt=""
|
||||
fill
|
||||
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
|
||||
loading="lazy"
|
||||
{...(ph ? { placeholder: 'blur' as const, blurDataURL: ph.blurDataURL } : {})}
|
||||
/>
|
||||
) : (
|
||||
<div className="skeleton" style={{ width: '100%', height: '100%' }} />
|
||||
<div
|
||||
className="skeleton"
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<span className="subpage-grid__item-badge">
|
||||
<span className="subpage-grid__item-badge" aria-hidden="true">
|
||||
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
||||
</span>
|
||||
<div className="subpage-grid__item-overlay">
|
||||
<div className="subpage-grid__item-overlay" aria-hidden="true">
|
||||
<span className="subpage-grid__item-title">{album.albumName}</span>
|
||||
<span className="subpage-grid__item-count">
|
||||
{album.assetCount} {album.assetCount === 1 ? 'photo' : 'photos'}
|
||||
@@ -126,23 +129,13 @@ export function SubpageGridView({
|
||||
{sec.description && <p className="subpage-section__desc">{sec.description}</p>}
|
||||
<div className="subpage-section__rule" aria-hidden="true" />
|
||||
</header>
|
||||
<AlbumGrid
|
||||
albums={sectionAlbums}
|
||||
albumMap={albumMap}
|
||||
placeholderMap={placeholderMap}
|
||||
slug={slug}
|
||||
/>
|
||||
<AlbumGrid albums={sectionAlbums} placeholderMap={placeholderMap} slug={slug} />
|
||||
</section>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
/* Flat layout (no sections) */
|
||||
<AlbumGrid
|
||||
albums={albums}
|
||||
albumMap={albumMap}
|
||||
placeholderMap={placeholderMap}
|
||||
slug={slug}
|
||||
/>
|
||||
<AlbumGrid albums={albums} placeholderMap={placeholderMap} slug={slug} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user