a11y: streamer-profile header — avatar wrap + live card keyboard-activatable
Two click-only divs in the new profile header (4.6.17+) had no keyboard equivalent: - .streamer-profile-avatar-wrap (clicking the avatar opens the channel on twitch.tv) — the only way to trigger that action besides the "Open on Twitch" button in the action column, so keyboard users were missing a primary affordance - .streamer-profile-live-card (clicking anywhere on the live preview card starts a live recording) — the embedded Record-now button inside the card already covered keyboard activation, so this one is more about completeness than necessity Both got: - role="button" + tabindex="0" - aria-label = the existing tooltip locale string (so AT reads the same text shown to sighted users on hover) - An inline onkeydown that re-fires the same onclick handler on Enter / Space. The live-card additionally checks event.target === event.currentTarget so a focused inner button pressing Enter doesn't double-fire the wrapper handler CSS adds focus-visible rings: - Purple ring on the avatar wrap (matching the existing avatar's purple border) - Red ring + glow on the live card (matching the existing card border colour) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5e369fef35
commit
96113dc267
@ -121,7 +121,7 @@ function renderStreamerProfileCard(p: StreamerProfile): void {
|
||||
// current preview frame + viewer count + title + game + record CTA.
|
||||
const liveCard = p.isLive
|
||||
? `
|
||||
<div class="streamer-profile-live-card" onclick="triggerLiveRecordingFromProfile('${safeLogin}')" title="${escapeProfileHtml(UI_TEXT.profile.liveCardTooltip)}">
|
||||
<div class="streamer-profile-live-card" role="button" tabindex="0" aria-label="${escapeProfileHtml(UI_TEXT.profile.liveCardTooltip)}" onclick="triggerLiveRecordingFromProfile('${safeLogin}')" onkeydown="if((event.key==='Enter'||event.key===' ')&&event.target===event.currentTarget){event.preventDefault();triggerLiveRecordingFromProfile('${safeLogin}');}" title="${escapeProfileHtml(UI_TEXT.profile.liveCardTooltip)}">
|
||||
${p.currentStreamPreviewUrl
|
||||
? `<img class="streamer-profile-live-thumb" src="${escapeProfileHtml(p.currentStreamPreviewUrl)}" alt="Live preview" onerror="onProfileLivePreviewError(this)">`
|
||||
: `<div class="streamer-profile-live-thumb-fallback"></div>`}
|
||||
@ -140,7 +140,7 @@ function renderStreamerProfileCard(p: StreamerProfile): void {
|
||||
applyProfileHtml(el, `
|
||||
${bannerStyle ? `<div class="streamer-profile-banner-bg" style="${bannerStyle}"></div>` : ''}
|
||||
<div class="streamer-profile-row">
|
||||
<div class="streamer-profile-avatar-wrap" onclick="openTwitchChannel('${safeUrl}')" title="${escapeProfileHtml(UI_TEXT.profile.openTwitchTooltip)}">
|
||||
<div class="streamer-profile-avatar-wrap" role="button" tabindex="0" aria-label="${escapeProfileHtml(UI_TEXT.profile.openTwitchTooltip)}" onclick="openTwitchChannel('${safeUrl}')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();openTwitchChannel('${safeUrl}');}" title="${escapeProfileHtml(UI_TEXT.profile.openTwitchTooltip)}">
|
||||
${avatarBlock}
|
||||
</div>
|
||||
<div class="streamer-profile-body">
|
||||
|
||||
@ -3340,6 +3340,17 @@ input[type="number"]::-webkit-outer-spin-button {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.streamer-profile-avatar-wrap:focus-visible {
|
||||
outline: none;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 3px rgba(145, 70, 255, 0.55);
|
||||
}
|
||||
|
||||
.streamer-profile-live-card:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(233, 25, 22, 0.55), 0 6px 22px rgba(233, 25, 22, 0.20);
|
||||
}
|
||||
|
||||
.streamer-profile-avatar {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user