a11y: exported semantics (forms/rating/nav/iframe/icons)
- InputField/TextareaField/ContactForm: every control gets a
deterministic id (slugId() in utils/escape.ts, derived from the
field's name/label + index for ContactForm's looped fields -- no
Math.random) with a matching <label for=>; fields with no visible
label get an aria-label from the placeholder/name instead.
- StarRating: wrapped in role="img" aria-label="Rating: N out of M",
individual star glyphs marked aria-hidden.
- Navbar: the mobile hamburger toggle gets aria-label="Toggle
navigation menu", aria-controls="navbar-links", and aria-expanded
wired to flip true/false in the inline onclick handler.
- VideoBlock and MapEmbed: every exported <iframe> gets a title
(generic "Embedded video", or "Map of {address}" for MapEmbed).
- Decorative Font Awesome icons (ContentSlider arrows already covered
in the prior commit; SocialLinks, SearchBar, Testimonials stars) are
aria-hidden; SocialLinks' icon-only links get an aria-label naming
the platform alongside the existing title tooltip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,9 +42,9 @@ function renderStars(count: number, color: string): React.ReactNode {
|
||||
|
||||
function starsHtml(count: number, color: string): string {
|
||||
const stars = [1, 2, 3, 4, 5].map((i) =>
|
||||
`<i class="fa ${i <= count ? 'fa-star' : 'fa-star-o'}" style="color:${color};font-size:14px"></i>`
|
||||
`<i class="fa ${i <= count ? 'fa-star' : 'fa-star-o'}" style="color:${color};font-size:14px" aria-hidden="true"></i>`
|
||||
).join('');
|
||||
return `<div style="display:flex;gap:2px;justify-content:center;margin-bottom:12px">${stars}</div>`;
|
||||
return `<div style="display:flex;gap:2px;justify-content:center;margin-bottom:12px" role="img" aria-label="Rating: ${count} out of 5">${stars}</div>`;
|
||||
}
|
||||
|
||||
export const Testimonials: UserComponent<TestimonialsProps> = ({
|
||||
|
||||
Reference in New Issue
Block a user