Preact: Improve screenreader support
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run

The page should be a lot more accessible now.

All the aria-hiddens may be distracting; the most important change is
role="tab" and role="tabpanel" for panels, and role="dialog" for
popups.
This commit is contained in:
Guangcong Luo 2025-04-25 10:30:39 +00:00
parent 2ec10bc172
commit e68993813d
18 changed files with 164 additions and 129 deletions

View File

@ -242,7 +242,7 @@ export class BattleScene implements BattleSceneStub {
this.stopAnimation();
this.updateBgm();
if (!this.battle.started) {
this.$frame.append('<div class="playbutton"><button name="play" class="button"><i class="fa fa-play"></i> Play</button><br /><br /><button name="play-muted" class="startsoundchooser button" style="font-size:10pt">Play (sound off)</button></div>');
this.$frame.append('<div class="playbutton"><button name="play" class="button"><i class="fa fa-play" aria-hidden="true"></i> Play</button><br /><br /><button name="play-muted" class="startsoundchooser button" style="font-size:10pt">Play (sound off)</button></div>');
this.$frame.find('div.playbutton button[name=play-muted]').click(() => {
this.setMute(true);
this.battle.play();

View File

@ -112,7 +112,7 @@ export class BattleLog {
this.skippedLines = true;
const el = document.createElement('div');
el.className = 'chat';
el.innerHTML = '<button class="button earlier-button"><i class="fa fa-caret-up"></i><br />Earlier messages</button>';
el.innerHTML = '<button class="button earlier-button"><i class="fa fa-caret-up" aria-hidden="true"></i><br />Earlier messages</button>';
const button = el.getElementsByTagName('button')[0];
button?.addEventListener?.('click', e => {
e.preventDefault();

View File

@ -381,7 +381,7 @@ export class PSSearchResults extends preact.Component<{ search: DexSearch }> {
Filters: {}
{search.filters.map(([type, name]) =>
<button class="filter" value={`${type}:${name}`}>
${name} <i class="fa fa-times-circle"></i>
${name} <i class="fa fa-times-circle" aria-hidden></i>
</button>
)}
{!search.query && <small style="color: #888">(backspace = delete filter)</small>}

View File

@ -58,7 +58,7 @@ class BattlesPanel extends PSRoomPanel<BattlesRoom> {
static readonly routes = ['battles'];
static readonly Model = BattlesRoom;
static readonly location = 'right';
static readonly icon = <i class="fa fa-caret-square-o-right"></i>;
static readonly icon = <i class="fa fa-caret-square-o-right" aria-hidden></i>;
static readonly title = 'Battles';
refresh = () => {
this.props.room.refresh();
@ -80,11 +80,13 @@ class BattlesPanel extends PSRoomPanel<BattlesRoom> {
const room = this.props.room;
return <PSPanelWrapper room={room} scrollable><div class="pad">
<button class="button" style="float:right;font-size:10pt;margin-top:3px" name="closeRoom">
<i class="fa fa-times"></i> Close
<i class="fa fa-times" aria-hidden></i> Close
</button>
<div class="roomlist">
<p>
<button class="button" name="refresh" onClick={this.refresh}><i class="fa fa-refresh"></i> Refresh</button> {}
<button class="button" name="refresh" onClick={this.refresh}>
<i class="fa fa-refresh" aria-hidden></i> Refresh
</button> {}
<span
style={Dex.getPokemonIcon('meloetta-pirouette') + ';display:inline-block;vertical-align:middle'} class="picon"
title="Meloetta is PS's mascot! The Pirouette forme is Fighting-type, and represents our battles."
@ -339,33 +341,33 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
<p>
{atEnd ? (
<button class="button disabled" data-cmd="/play" style="min-width:4.5em">
<i class="fa fa-play"></i><br />Play
<i class="fa fa-play" aria-hidden></i><br />Play
</button>
) : room.battle.paused ? (
<button class="button" data-cmd="/play" style="min-width:4.5em">
<i class="fa fa-play"></i><br />Play
<i class="fa fa-play" aria-hidden></i><br />Play
</button>
) : (
<button class="button" data-cmd="/pause" style="min-width:4.5em">
<i class="fa fa-pause"></i><br />Pause
<i class="fa fa-pause" aria-hidden></i><br />Pause
</button>
)} {}
<button class={"button button-first" + (atStart ? " disabled" : "")} data-cmd="/ffto 0" style="margin-right:2px">
<i class="fa fa-undo"></i><br />First turn
<i class="fa fa-undo" aria-hidden></i><br />First turn
</button>
<button class={"button button-first" + (atStart ? " disabled" : "")} data-cmd="/ffto -1">
<i class="fa fa-step-backward"></i><br />Prev turn
<i class="fa fa-step-backward" aria-hidden></i><br />Prev turn
</button>
<button class={"button button-last" + (atEnd ? " disabled" : "")} data-cmd="/ffto +1" style="margin-right:2px">
<i class="fa fa-step-forward"></i><br />Skip turn
<i class="fa fa-step-forward" aria-hidden></i><br />Skip turn
</button>
<button class={"button button-last" + (atEnd ? " disabled" : "")} data-cmd="/ffto end">
<i class="fa fa-fast-forward"></i><br />Skip to end
<i class="fa fa-fast-forward" aria-hidden></i><br />Skip to end
</button>
</p>
<p>
<button class="button" data-cmd="/switchsides">
<i class="fa fa-random"></i> Switch viewpoint
<i class="fa fa-random" aria-hidden></i> Switch viewpoint
</button>
</p>
</div>;
@ -506,7 +508,7 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
if (choices.isEmpty()) return null;
let buf: preact.ComponentChild[] = [
<button data-cmd="/cancel" class="button"><i class="fa fa-chevron-left"></i> Back</button>, ' ',
<button data-cmd="/cancel" class="button"><i class="fa fa-chevron-left" aria-hidden></i> Back</button>, ' ',
];
if (choices.isDone() && request.noCancel) {
buf = ['Waiting for opponent...', <br />];
@ -567,7 +569,9 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
if (choices.isDone()) {
return <div class="controls">
<div class="whatdo">
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
<button name="openTimer" class="button disabled timerbutton">
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
</button>
{this.renderOldChoices(request, choices)}
</div>
<div class="pad">
@ -595,7 +599,9 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
const moveName = choices.getChosenMove(choices.current, choices.index()).name;
return <div class="controls">
<div class="whatdo">
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
<button name="openTimer" class="button disabled timerbutton">
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
</button>
{this.renderOldChoices(request, choices)}
{pokemon.name} should use <strong>{moveName}</strong> at where? {}
</div>
@ -611,7 +617,9 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
return <div class="controls">
<div class="whatdo">
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
<button name="openTimer" class="button disabled timerbutton">
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
</button>
{this.renderOldChoices(request, choices)}
What will <strong>{pokemon.name}</strong> do?
</div>
@ -666,7 +674,9 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
const pokemon = request.side.pokemon[choices.index()];
return <div class="controls">
<div class="whatdo">
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
<button name="openTimer" class="button disabled timerbutton">
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
</button>
{this.renderOldChoices(request, choices)}
What will <strong>{pokemon.name}</strong> do?
</div>
@ -680,9 +690,11 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
} case 'team': {
return <div class="controls">
<div class="whatdo">
<button name="openTimer" class="button disabled timerbutton"><i class="fa fa-hourglass-start"></i> Timer</button>
<button name="openTimer" class="button disabled timerbutton">
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
</button>
{choices.alreadySwitchingIn.length > 0 ? (
[<button data-cmd="/cancel" class="button"><i class="fa fa-chevron-left"></i> Back</button>,
[<button data-cmd="/cancel" class="button"><i class="fa fa-chevron-left" aria-hidden></i> Back</button>,
" What about the rest of your team? "]
) : (
"How will you start the battle? "
@ -720,22 +732,22 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
href={`//${Config.routes.replays}/download`}
class="button replayDownloadButton"
>
<i class="fa fa-download"></i> Download replay</a>
<i class="fa fa-download" aria-hidden></i> Download replay</a>
<br />
<br />
<button class="button" data-cmd="/savereplay">
<i class="fa fa-upload"></i> Upload and share replay
<i class="fa fa-upload" aria-hidden></i> Upload and share replay
</button>
</span>
<button class="button" data-cmd="/play" style="min-width:4.5em">
<i class="fa fa-undo"></i><br />Replay
<i class="fa fa-undo" aria-hidden></i><br />Replay
</button> {}
{isNotTiny && <button class="button button-first" data-cmd="/ffto 0" style="margin-right:2px">
<i class="fa fa-undo"></i><br />First turn
<i class="fa fa-undo" aria-hidden></i><br />First turn
</button>}
{isNotTiny && <button class="button button-first" data-cmd="/ffto -1">
<i class="fa fa-step-backward"></i><br />Prev turn
<i class="fa fa-step-backward" aria-hidden></i><br />Prev turn
</button>}
</p>
{room.side ? (
@ -749,7 +761,7 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
</p>
) : (
<p>
<button class="button" data-cmd="/switchsides"><i class="fa fa-random"></i> Switch viewpoint</button>
<button class="button" data-cmd="/switchsides"><i class="fa fa-random" aria-hidden></i> Switch viewpoint</button>
</p>
)}
</div>;

View File

@ -473,7 +473,7 @@ export class TournamentBox extends preact.Component<{ tour: ChatTournament, left
onSubmit={this.acceptChallenge} onValidate={this.validate}
>
{(info.isJoined && !info.challenging && !info.challenged && !info.challenges?.length) && (
<button name="validate" class="button"><i class="fa fa-check"></i> Validate</button>
<button name="validate" class="button"><i class="fa fa-check" aria-hidden></i> Validate</button>
)} {}
{!!(!info.isStarted && info.isJoined) && (
<button data-cmd="/tournament leave" class="button">Leave</button>
@ -514,7 +514,7 @@ export class TournamentBox extends preact.Component<{ tour: ChatTournament, left
<button class="tournament-title" onClick={this.toggleBoxVisibility}>
<span class="tournament-status">{info.isStarted ? "In Progress" : "Signups"}</span>
{tour.tournamentName()}
{tour.boxVisible ? <i class="fa fa-caret-up"></i> : <i class="fa fa-caret-down"></i>}
{tour.boxVisible ? <i class="fa fa-caret-up" aria-hidden></i> : <i class="fa fa-caret-down" aria-hidden></i>}
</button>
<div class={`tournament-box ${tour.boxVisible ? 'active' : ''}`}>
<TournamentBracket tour={tour} />
@ -544,7 +544,7 @@ export class TournamentBracket extends preact.Component<{
<tr>
<th>{row.name}</th><td>{row.score}</td>
<td class="tournament-bracket-table-cell-null">{i < 3 ? (
<i class="fa fa-trophy" style={{ color: ['#d6c939', '#adb2bb', '#ca8530'][i] }}></i>
<i class="fa fa-trophy" aria-hidden style={{ color: ['#d6c939', '#adb2bb', '#ca8530'][i] }}></i>
) : null}</td>
</tr>
))}
@ -657,9 +657,15 @@ export class TournamentBracket extends preact.Component<{
{data?.type === 'table' ? this.renderTableBracket(data) :
data?.type === 'tree' ? <TournamentTreeBracket data={data} abbreviated={this.props.abbreviated} /> :
null}
{this.props.poppedOut ?
<button class="tournament-close-link button" data-cmd="/close"><i class="fa fa-times"></i> Close</button> :
<button class="tournament-popout-link button" onClick={this.popOut}><i class="fa fa-arrows-alt"></i> Pop-out</button>}
{this.props.poppedOut ? (
<button class="tournament-close-link button" data-cmd="/close">
<i class="fa fa-times" aria-hidden></i> Close
</button>
) : (
<button class="tournament-popout-link button" onClick={this.popOut}>
<i class="fa fa-arrows-alt" aria-hidden></i> Pop-out
</button>
)}
</div>;
}
}

View File

@ -974,7 +974,7 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
static readonly routes = ['dm-*', 'groupchat-*', '*'];
static readonly Model = ChatRoom;
static readonly location = 'right';
static readonly icon = <i class="fa fa-comment-o"></i>;
static readonly icon = <i class="fa fa-comment-o" aria-hidden></i>;
override componentDidMount(): void {
super.componentDidMount();
this.subscribeTo(PS.user, () => {
@ -1045,7 +1045,7 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
return <PSPanelWrapper room={room} focusClick>
<ChatLog class="chat-log" room={this.props.room} left={tinyLayout ? 0 : 146} top={room.tour?.info.isActive ? 30 : 0}>
{challengeTo}{challengeFrom}{PS.isOffline && <p class="buttonbar">
<button class="button" data-cmd="/reconnect"><i class="fa fa-plug"></i> <strong>Reconnect</strong></button>
<button class="button" data-cmd="/reconnect"><i class="fa fa-plug" aria-hidden></i> <strong>Reconnect</strong></button>
</p>}
</ChatLog>
{room.tour && <TournamentBox tour={room.tour} left={tinyLayout ? 0 : 146} />}

View File

@ -97,7 +97,7 @@ class LadderFormatPanel extends PSRoomPanel<LadderFormatRoom> {
static readonly id = 'ladderformat';
static readonly routes = ['ladder-*'];
static readonly Model = LadderFormatRoom;
static readonly icon = <i class="fa fa-list-ol"></i>;
static readonly icon = <i class="fa fa-list-ol" aria-hidden></i>;
override componentDidMount() {
const { room } = this.props;
@ -156,7 +156,7 @@ class LadderFormatPanel extends PSRoomPanel<LadderFormatRoom> {
const room = this.props.room;
if (room.loading || !BattleFormats) {
return <p><i class="fa fa-refresh fa-spin"></i> <em>Loading...</em></p>;
return <p><i class="fa fa-refresh fa-spin" aria-hidden></i> <em>Loading...</em></p>;
} else if (room.error !== undefined) {
return <p>Error: {room.error}</p>;
} else if (!room.ladderData) {
@ -179,7 +179,7 @@ class LadderFormatPanel extends PSRoomPanel<LadderFormatRoom> {
</tr>
{room.ladderData.toplist.map((row, i) => <tr>
<td style={{ textAlign: 'right' }}>
{i < 3 && <i class="fa fa-trophy" style={{ color: ['#d6c939', '#adb2bb', '#ca8530'][i] }}></i>} {i + 1}
{i < 3 && <i class="fa fa-trophy" aria-hidden style={{ color: ['#d6c939', '#adb2bb', '#ca8530'][i] }}></i>} {i + 1}
</td>
<td><span
class="username no-interact" style={{
@ -204,14 +204,14 @@ class LadderFormatPanel extends PSRoomPanel<LadderFormatRoom> {
<div class="ladder pad">
<p>
<button class="button" data-href="ladder" data-target="replace">
<i class="fa fa-chevron-left"></i> Format List
<i class="fa fa-chevron-left" aria-hidden></i> Format List
</button>
</p>
<p>
<button class="button" data-href="ladder" data-target="replace">
<i class="fa fa-refresh"></i> Refresh
<i class="fa fa-refresh" aria-hidden></i> Refresh
</button> <a class="button" href="/view-seasonladder-gen9randombattle">
<i class="fa fa-trophy"></i> Seasonal rankings
<i class="fa fa-trophy" aria-hidden></i> Seasonal rankings
</a>
{this.renderSearch()}
</p>
@ -225,7 +225,7 @@ class LadderFormatPanel extends PSRoomPanel<LadderFormatRoom> {
class LadderListPanel extends PSRoomPanel {
static readonly id = 'ladder';
static readonly routes = ['ladder'];
static readonly icon = <i class="fa fa-list-ol"></i>;
static readonly icon = <i class="fa fa-list-ol" aria-hidden></i>;
static readonly title = 'Ladder';
override componentDidMount() {
@ -262,7 +262,7 @@ class LadderListPanel extends PSRoomPanel {
</p>
<p>
<button data-href="view-ladderhelp" class="button">
<i class="fa fa-info-circle"></i> How the ladder works
<i class="fa fa-info-circle" aria-hidden></i> How the ladder works
</button>
</p>
{this.renderList()}

View File

@ -434,7 +434,7 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
static readonly id = 'mainmenu';
static readonly routes = [''];
static readonly Model = MainMenuRoom;
static readonly icon = <i class="fa fa-home"></i>;
static readonly icon = <i class="fa fa-home" aria-hidden></i>;
override focus() {
this.base?.querySelector<HTMLButtonElement>('.formatselect')?.focus();
}
@ -504,10 +504,14 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
class={`mini-window-header${notifying}`} draggable onDragStart={this.handleDragStart} onClick={this.handleClickMinimize}
>
<button class="closebutton" data-cmd="/close" aria-label="Close" tabIndex={-1}>
<i class="fa fa-times-circle"></i>
<i class="fa fa-times-circle" aria-hidden></i>
</button>
<button class="maximizebutton" data-cmd="/maximize" tabIndex={-1} aria-label="Maximize">
<i class="fa fa-stop-circle" aria-hidden></i>
</button>
<button class="minimizebutton" tabIndex={-1} aria-label="Expand/Collapse">
<i class="fa fa-minus-circle" aria-hidden></i>
</button>
<button class="maximizebutton" data-cmd="/maximize" tabIndex={-1}><i class="fa fa-stop-circle"></i></button>
<button class="minimizebutton" tabIndex={-1}><i class="fa fa-minus-circle"></i></button>
{room.title}
</h3>
{this.renderMiniRoom(room)}
@ -547,12 +551,12 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
return <TeamForm class="menugroup" onSubmit={this.submitSearch}>
<button class="mainmenu1 mainmenu big button disabled" disabled name="search">
<em>{PS.isOffline ? [<span class="fa-stack fa-lg">
<i class="fa fa-plug fa-flip-horizontal fa-stack-1x"></i>
<i class="fa fa-ban fa-stack-2x text-danger"></i>
<i class="fa fa-plug fa-flip-horizontal fa-stack-1x" aria-hidden></i>
<i class="fa fa-ban fa-stack-2x text-danger" aria-hidden></i>
</span>, " Disconnected"] : "Connecting..."}</em>
</button>
{PS.isOffline && <p class="buttonbar">
<button class="button" data-cmd="/reconnect"><i class="fa fa-plug"></i> <strong>Reconnect</strong></button>
<button class="button" data-cmd="/reconnect"><i class="fa fa-plug" aria-hidden></i> <strong>Reconnect</strong></button>
</p>}
</TeamForm>;
}
@ -560,15 +564,15 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
return <TeamForm class="menugroup" onSubmit={this.submitSearch}>
{PS.mainmenu.searchCountdown ? (
<>
<button class="mainmenu1 mainmenu big button disabled" type="submit">
<strong><i class="fa fa-refresh fa-spin"></i> Searching in {PS.mainmenu.searchCountdown.countdown}...</strong>
</button>
<button class="mainmenu1 mainmenu big button disabled" type="submit"><strong>
<i class="fa fa-refresh fa-spin" aria-hidden></i> Searching in {PS.mainmenu.searchCountdown.countdown}...
</strong></button>
<p class="buttonbar"><button class="button" data-cmd="/cancelsearch">Cancel</button></p>
</>
) : (PS.mainmenu.searchSent || PS.mainmenu.search.searching.length) ? (
<>
<button class="mainmenu1 mainmenu big button disabled" type="submit">
<strong><i class="fa fa-refresh fa-spin"></i> Searching...</strong>
<strong><i class="fa fa-refresh fa-spin" aria-hidden></i> Searching...</strong>
</button>
<p class="buttonbar"><button class="button" data-cmd="/cancelsearch">Cancel</button></p>
</>

View File

@ -48,7 +48,7 @@ function PageLadderHelp() {
return <div class="ladder pad">
<p>
<button class="button" data-href="/ladder" data-target="replace">
<i class="fa fa-chevron-left"></i> Format List
<i class="fa fa-chevron-left" aria-hidden></i> Format List
</button>
</p>
<h3>How the ladder works</h3>

View File

@ -149,8 +149,8 @@ class UserPanel extends PSRoomPanel<UserRoom> {
buttonbar.push(
<hr />,
<p class="buttonbar" style="text-align: right">
<button class="button" data-href="login"><i class="fa fa-pencil"></i> Change name</button> {}
<button class="button" data-cmd="/logout"><i class="fa fa-power-off"></i> Log out</button>
<button class="button" data-href="login"><i class="fa fa-pencil" aria-hidden></i> Change name</button> {}
<button class="button" data-cmd="/logout"><i class="fa fa-power-off" aria-hidden></i> Log out</button>
</p>
);
}
@ -369,7 +369,7 @@ class UserOptionsPanel extends PSRoomPanel {
</small>
<p class="buttonbar">
<button class="button" onClick={this.handleConfirm}>
<i class="fa fa-confirm"></i> Confirm
<i class="fa fa-confirm" aria-hidden></i> Confirm
</button> {}
<button class="button" onClick={this.handleCancel}>
Cancel
@ -389,7 +389,7 @@ class UserOptionsPanel extends PSRoomPanel {
</div>
) : (
<button class="button" onClick={this.handleMute}>
<i class="fa fa-hourglass-half"></i> Mute
<i class="fa fa-hourglass-half" aria-hidden></i> Mute
</button>
))} {}
{canBan && !this.state.showMuteInput && !this.state.showConfirm && (this.state.showBanInput ? (
@ -404,7 +404,7 @@ class UserOptionsPanel extends PSRoomPanel {
</div>
) : (
<button class="button" onClick={this.handleBan}>
<i class="fa fa-gavel"></i> Ban
<i class="fa fa-gavel" aria-hidden></i> Ban
</button>
))}
</p>
@ -611,7 +611,7 @@ class OptionsPanel extends PSRoomPanel {
{this.state.showStatusInput ? (
<p>
<input name="statustext" />
<button class="button" onClick={this.editStatus}><i class="fa fa-pencil"></i></button>
<button class="button" onClick={this.editStatus}><i class="fa fa-pencil" aria-hidden></i></button>
</p>
) : (
<p>
@ -744,10 +744,10 @@ class OptionsPanel extends PSRoomPanel {
</p>
<hr />
{PS.user.named ? <p class="buttonbar" style="text-align: right">
<button class="button" data-href="login"><i class="fa fa-pencil"></i> Change name</button> {}
<button class="button" data-cmd="/logout"><i class="fa fa-power-off"></i> Log out</button>
<button class="button" data-href="login"><i class="fa fa-pencil" aria-hidden></i> Change name</button> {}
<button class="button" data-cmd="/logout"><i class="fa fa-power-off" aria-hidden></i> Log out</button>
</p> : <p class="buttonbar" style="text-align: right">
<button class="button" data-href="login"><i class="fa fa-pencil"></i> Choose name</button>
<button class="button" data-href="login"><i class="fa fa-pencil" aria-hidden></i> Choose name</button>
</p> }
</div></PSPanelWrapper>;
}
@ -857,7 +857,7 @@ class LoginPanel extends PSRoomPanel {
<small>(Others will be able to see your name change. To change name privately, use "Log out")</small>
</p>}
{loginState?.needsPassword && <p>
<i class="fa fa-level-up fa-rotate-90"></i> <strong>if you registered this name:</strong>
<i class="fa fa-level-up fa-rotate-90" aria-hidden></i> <strong>if you registered this name:</strong>
<label class="label">
Password: {}
<input
@ -867,11 +867,11 @@ class LoginPanel extends PSRoomPanel {
<button
type="button" onClick={this.handleShowPassword} aria-label="Show password"
class="button" style="float:right;margin:-21px 0 10px;padding: 2px 6px"
><i class="fa fa-eye"></i></button>
><i class="fa fa-eye" aria-hidden></i></button>
</label>
</p>}
{loginState?.needsGoogle && <>
<p><i class="fa fa-level-up fa-rotate-90"></i> <strong>if you registered this name:</strong></p>
<p><i class="fa fa-level-up fa-rotate-90" aria-hidden></i> <strong>if you registered this name:</strong></p>
<p><GooglePasswordBox name={this.getUsername()} /></p>
</>}
<p class="buttonbar">
@ -893,7 +893,7 @@ class LoginPanel extends PSRoomPanel {
</p>
{loginState?.name && <div>
<p>
<i class="fa fa-level-up fa-rotate-90"></i> <strong>if not:</strong>
<i class="fa fa-level-up fa-rotate-90" aria-hidden></i> <strong>if not:</strong>
</p>
<p style={{ maxWidth: '210px', margin: '0 auto' }}>
This is someone else's account. Sorry.
@ -1501,10 +1501,10 @@ class RoomTabListPanel extends PSRoomPanel {
const verticalTabs = PS.prefs.onepanel === 'vertical';
return <PSPanelWrapper room={this.props.room}><div class="tablist">
<ul>
{PS.leftRoomList.map(roomid => PSHeader.renderRoomTab(roomid))}
{PS.leftRoomList.map(roomid => PSHeader.renderRoomTab(roomid, true))}
</ul>
<ul>
{PS.rightRoomList.map(roomid => PSHeader.renderRoomTab(roomid))}
{PS.rightRoomList.map(roomid => PSHeader.renderRoomTab(roomid, true))}
</ul>
<div class="pad"><label class="checkbox"><input
type="checkbox" checked={verticalTabs} onChange={this.handleLayoutChange}

View File

@ -23,7 +23,7 @@ class RoomsPanel extends PSRoomPanel {
static readonly routes = ['rooms'];
static readonly Model = RoomsRoom;
static readonly location = 'right';
static readonly icon = <i class="fa fa-plus rooms-plus"></i>;
static readonly icon = <i class="fa fa-plus rooms-plus" aria-hidden></i>;
static readonly title = "Chat Rooms";
hidden = false;
search = '';
@ -154,7 +154,7 @@ class RoomsPanel extends PSRoomPanel {
return <PSPanelWrapper room={this.props.room} scrollable><div class="pad">
<button class="button" style="float:right;font-size:10pt;margin-top:3px" onClick={this.hide}>
<i class="fa fa-caret-right"></i> Hide
<i class="fa fa-caret-right" aria-hidden></i> Hide
</button>
<div class="roomcounters">
<a class="button" href="users" title="Find an online user">
@ -191,13 +191,13 @@ class RoomsPanel extends PSRoomPanel {
{sortedRooms.map(roomInfo => <div key={roomInfo.title}>
<a href={`/${toID(roomInfo.title)}`} class="blocklink">
{roomInfo.userCount !== undefined && <small style="float:right">({roomInfo.userCount} users)</small>}
<strong><i class="fa fa-comment-o"></i> {roomInfo.title}<br /></strong>
<strong><i class="fa fa-comment-o" aria-hidden></i> {roomInfo.title}<br /></strong>
<small>{roomInfo.desc || ''}</small>
</a>
{roomInfo.subRooms && <div class="subrooms">
<i class="fa fa-level-up fa-rotate-90"></i> Subrooms: {}
<i class="fa fa-level-up fa-rotate-90" aria-hidden></i> Subrooms: {}
{roomInfo.subRooms.map((roomName, i) => [<a href={`/${toID(roomName)}`} class="blocklink">
<i class="fa fa-comment-o"></i> <strong>{roomName}</strong>
<i class="fa fa-comment-o" aria-hidden></i> <strong>{roomName}</strong>
</a>, ' '])}
</div>}
</div>)}

View File

@ -645,7 +645,9 @@ class TeamTextbox extends preact.Component<{ team: Team, room: TeamRoom }> {
{this.setInfo.length < 6 && [
!!this.setInfo.length && <hr style={`top:${this.bottomY() - 18}px`} />,
<div style={`top:${this.bottomY() - 3}px ;left:105px;position:absolute`}>
<button class="button" onClick={this.addPokemon}><i class="fa fa-plus"></i> Add Pok&eacute;mon</button>
<button class="button" onClick={this.addPokemon}>
<i class="fa fa-plus" aria-hidden></i> Add Pok&eacute;mon
</button>
</div>,
]}
{this.selection?.active && this.selection.offsetY !== null && (
@ -657,7 +659,9 @@ class TeamTextbox extends preact.Component<{ team: Team, room: TeamRoom }> {
class="searchresults" style={{ top: (this.setInfo[this.selection.setIndex]?.bottomY ?? this.bottomY() + 50) - 12 }}
onClick={this.handleClick}
>
<button class="button closesearch" onClick={this.closeMenu}><i class="fa fa-times"></i> Close</button>
<button class="button closesearch" onClick={this.closeMenu}>
<i class="fa fa-times" aria-hidden></i> Close
</button>
{this.selection.type === 'stats' ? (
<StatForm room={this.props.room} set={this.sets[this.selection.setIndex]} onChange={this.handleSetChange} />
) : this.selection.type === 'details' ? (
@ -711,7 +715,7 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
if (!room.team) {
return <PSPanelWrapper room={room}>
<a class="button" href="teambuilder" data-target="replace">
<i class="fa fa-chevron-left"></i> List
<i class="fa fa-chevron-left" aria-hidden></i> List
</a>
<p class="error">
Team doesn't exist
@ -723,7 +727,7 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
const formatName = BattleLog.formatName(room.team.format);
return <PSPanelWrapper room={room} scrollable><div class="pad">
<a class="button" href="teambuilder" data-target="replace">
<i class="fa fa-chevron-left"></i> List
<i class="fa fa-chevron-left" aria-hidden></i> List
</a>
<div style="float:right"><FormatDropdown
format={room.team.format} placeholder="" selectType="teambuilder" onChange={this.handleChangeFormat}
@ -1147,7 +1151,7 @@ class StatForm extends preact.Component<{
/></td>
<td><input
name={`evslider-${statID}`} value={set.evs?.[statID] ?? defaultEV} min="0" max={maxEV} step={stepEV}
type="range" class="evslider" tabIndex={-1} aria-hidden="true" style="width:170px"
type="range" class="evslider" tabIndex={-1} aria-hidden style="width:170px"
onInput={this.changeEV} onChange={this.changeEV}
/></td>
<td><input

View File

@ -75,7 +75,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
static readonly id = 'teambuilder';
static readonly routes = ['teambuilder'];
static readonly Model = TeambuilderRoom;
static readonly icon = <i class="fa fa-pencil-square-o"></i>;
static readonly icon = <i class="fa fa-pencil-square-o" aria-hidden></i>;
static readonly title = 'Teambuilder';
selectFolder = (e: MouseEvent) => {
const room = this.props.room;
@ -141,7 +141,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
let renderedFormatFolders = [
<div class="foldersep"></div>,
<TeamFolder cur={false} value="+">
<i class="fa fa-plus"></i><em>(add format folder)</em>
<i class="fa fa-plus" aria-hidden></i><em>(add format folder)</em>
</TeamFolder>,
];
@ -188,7 +188,7 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
{renderedFolders}
<div class="foldersep"></div>
<TeamFolder cur={false} value="++">
<i class="fa fa-plus"></i><em>(add folder)</em>
<i class="fa fa-plus" aria-hidden></i><em>(add folder)</em>
</TeamFolder>
<div class="folderlistafter"></div>
@ -223,38 +223,38 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
<div class="teampane">
{filterFolder ? (
<h2>
<i class="fa fa-folder-open"></i> {filterFolder} {}
<i class="fa fa-folder-open" aria-hidden></i> {filterFolder} {}
<button class="button small" style="margin-left:5px" name="renameFolder">
<i class="fa fa-pencil"></i> Rename
<i class="fa fa-pencil" aria-hidden></i> Rename
</button> {}
<button class="button small" style="margin-left:5px" name="promptDeleteFolder">
<i class="fa fa-times"></i> Remove
<i class="fa fa-times" aria-hidden></i> Remove
</button>
</h2>
) : filterFolder === '' ? (
<h2><i class="fa fa-folder-open-o"></i> Teams not in any folders</h2>
<h2><i class="fa fa-folder-open-o" aria-hidden></i> Teams not in any folders</h2>
) : filterFormat ? (
<h2><i class="fa fa-folder-open-o"></i> {filterFormat} <small>({teams.length})</small></h2>
<h2><i class="fa fa-folder-open-o" aria-hidden></i> {filterFormat} <small>({teams.length})</small></h2>
) : (
<h2>All Teams <small>({teams.length})</small></h2>
)}
<p>
<button data-cmd="/newteam" class="button big"><i class="fa fa-plus-circle"></i> New Team</button>
<button data-cmd="/newteam" class="button big"><i class="fa fa-plus-circle" aria-hidden></i> New Team</button>
</p>
<ul class="teamlist">
{teams.map(team => team ? (
<li key={team.key}>
<TeamBox team={team} /> {}
<button data-cmd={`/deleteteam ${team.key}`}><i class="fa fa-trash"></i> Delete</button>
<button data-cmd={`/deleteteam ${team.key}`}><i class="fa fa-trash" aria-hidden></i> Delete</button>
</li>
) : (
<li key="undelete">
<button data-cmd="/undeleteteam"><i class="fa fa-undo"></i> Undo delete</button>
<button data-cmd="/undeleteteam"><i class="fa fa-undo" aria-hidden></i> Undo delete</button>
</li>
))}
</ul>
<p>
<button data-cmd="/newteam bottom" class="button"><i class="fa fa-plus-circle"></i> New Team</button>
<button data-cmd="/newteam bottom" class="button"><i class="fa fa-plus-circle" aria-hidden></i> New Team</button>
</p>
</div>
</PSPanelWrapper>;

View File

@ -731,18 +731,18 @@ class TeamDropdownPanel extends PSRoomPanel {
class={'button' + (baseFormat === this.format ? ' disabled' : '')}
onClick={this.setFormat} name="format" value={baseFormat}
>
<i class="fa fa-folder-o"></i> [{baseFormat.slice(0, 4)}] {baseFormat.slice(4)}
<i class="fa fa-folder-o" aria-hidden></i> [{baseFormat.slice(0, 4)}] {baseFormat.slice(4)}
</button>
)} {}
<button
class={'button' + (baseGen === this.format ? ' disabled' : '')} onClick={this.setFormat} name="format" value={baseGen}
>
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(uncategorized)</em>
<i class="fa fa-folder-o" aria-hidden></i> [{baseGen}] <em>(uncategorized)</em>
</button> {}
<button
class={'button' + (baseGen === this.gen ? ' disabled' : '')} onClick={this.setFormat} name="gen" value={baseGen}
>
<i class="fa fa-folder-o"></i> [{baseGen}] <em>(all)</em>
<i class="fa fa-folder-o" aria-hidden></i> [{baseGen}] <em>(all)</em>
</button> {}
{hasOtherGens && !this.gen && (
<button class="button" onClick={this.setFormat} name="gen" value={baseGen}>Other gens</button>
@ -753,7 +753,7 @@ class TeamDropdownPanel extends PSRoomPanel {
teamList.push(<h2>Other gens</h2>);
teamList.push(<p>{genList.sort().map(gen => [
<button class={'button' + (gen === this.gen ? ' disabled' : '')} onClick={this.setFormat} name="gen" value={gen}>
<i class="fa fa-folder-o"></i> [{gen}] <em>(all)</em>
<i class="fa fa-folder-o" aria-hidden></i> [{gen}] <em>(all)</em>
</button>,
" ",
])}</p>);
@ -763,20 +763,20 @@ class TeamDropdownPanel extends PSRoomPanel {
for (let folder in teamBuckets) {
if (folder && (this.gen || this.format)) {
teamList.push(<h2>
<i class="fa fa-folder-open"></i> {folder} + {}
<i class="fa fa-folder-open-o"></i> {this.format || this.gen}
<i class="fa fa-folder-open" aria-hidden></i> {folder} + {}
<i class="fa fa-folder-open-o" aria-hidden></i> {this.format || this.gen}
</h2>);
} else if (folder) {
teamList.push(<h2>
<i class="fa fa-folder-open"></i> {folder}
<i class="fa fa-folder-open" aria-hidden></i> {folder}
</h2>);
} else if (this.gen || this.format) {
teamList.push(<h2>
<i class="fa fa-folder-open-o"></i> {this.format || this.gen}
<i class="fa fa-folder-open-o" aria-hidden></i> {this.format || this.gen}
</h2>);
} else {
teamList.push(<h2>
<i class="fa fa-folder-open-o"></i> Teams not in any folders
<i class="fa fa-folder-open-o" aria-hidden></i> Teams not in any folders
</h2>);
}
teamList.push(<ul class="teamdropdown" onClick={this.click}>

View File

@ -84,7 +84,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
};
static roomInfo(room: PSRoom) {
const RoomType = PS.roomTypes[room.type];
let icon = RoomType?.icon || <i class="fa fa-file-text-o"></i>;
let icon = RoomType?.icon || <i class="fa fa-file-text-o" aria-hidden></i>;
let title = room.title;
switch (room.type) {
case 'battle':
@ -124,7 +124,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
}
return { icon, title };
}
static renderRoomTab(id: RoomID) {
static renderRoomTab(id: RoomID, noAria?: boolean) {
const room = PS.rooms[id];
if (!room) return null;
const closable = (id === '' || id === 'rooms' ? '' : ' closable');
@ -148,15 +148,18 @@ export class PSHeader extends preact.Component<{ style: object }> {
let closeButton = null;
if (closable) {
closeButton = <button class="closebutton" name="closeRoom" value={id} aria-label="Close">
<i class="fa fa-times-circle"></i>
<i class="fa fa-times-circle" aria-hidden></i>
</button>;
}
const ariaLabel = id === 'rooms' ? { "aria-label": "Join chat" } : {};
const aria: Record<string, string> = noAria ? {} : {
"role": "tab", "id": `roomtab-${id}`, "aria-selected": cur ? "true" : "false",
};
if (id === 'rooms') aria['aria-label'] = "Join chat";
return <li class={id === '' ? 'home-li' : ''}>
<a
class={className} href={`/${id}`} draggable={true} title={hoverTitle || undefined}
onDragEnter={this.handleDragEnter} onDragStart={this.handleDragStart}
{...ariaLabel}
{...aria}
>
{icon} {roomTitle}
</a>
@ -204,7 +207,9 @@ export class PSHeader extends preact.Component<{ style: object }> {
</span>;
}
renderVertical() {
return <div id="header" class="header-vertical" style={this.props.style} onClick={PSView.scrollToHeader}>
return <div
id="header" class="header-vertical" style={this.props.style} onClick={PSView.scrollToHeader} role="navigation"
>
<div class="maintabbarbottom"></div>
<div class="scrollable-part">
<img
@ -232,7 +237,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
<i class={PS.prefs.mute ? 'fa fa-volume-off' : 'fa fa-volume-up'}></i>
</button> {}
<button class="icon button" data-href="options" title="Options" aria-label="Options">
<i class="fa fa-cog"></i>
<i class="fa fa-cog" aria-hidden></i>
</button>
</div>
</div>
@ -248,7 +253,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
document.documentElement.classList?.remove('scroll-snap-enabled');
}
return <div id="header" class="header" style={this.props.style}>
return <div id="header" class="header" style={this.props.style} role="navigation">
<div class="maintabbarbottom"></div>
<div class="tabbar maintabbar"><div class="inner-1"><div class="inner-2">
<ul class="maintabbar-left" style={{ width: `${PS.leftPanelWidth}px` }}>
@ -269,7 +274,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
</div></div></div>
<div class="overflow">
<button name="tablist" class="button" data-href="roomtablist" aria-label="All tabs" type="button">
<i class="fa fa-caret-down"></i>
<i class="fa fa-caret-down" aria-hidden></i>
</button>
</div>
<div class="userbar">
@ -278,7 +283,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
<i class={PS.prefs.mute ? 'fa fa-volume-off' : 'fa fa-volume-up'}></i>
</button> {}
<button class="icon button" data-href="options" title="Options" aria-label="Options">
<i class="fa fa-cog"></i>
<i class="fa fa-cog" aria-hidden></i>
</button>
</div>
</div>;
@ -309,18 +314,18 @@ export class PSMiniHeader extends preact.Component {
null
) : window.scrollX ? (
<button onClick={PSView.scrollToHeader} class={`mini-header-left ${notifying}`} aria-label="Menu">
<i class="fa fa-arrow-left"></i>
<i class="fa fa-arrow-left" aria-hidden></i>
</button>
) : (
<button onClick={PSView.scrollToRoom} class="mini-header-left" aria-label="Menu">
<i class="fa fa-arrow-right"></i>
<i class="fa fa-arrow-right" aria-hidden></i>
</button>
);
return <div class="mini-header" style={{ minWidth: `${minWidth}px` }}>
{menuButton}
{icon} {title}
<button data-href="options" class="mini-header-right" aria-label="Options">
{PS.user.named ? <strong style={userColor}>{PS.user.name}</strong> : <i class="fa fa-cog"></i>}
{PS.user.named ? <strong style={userColor}>{PS.user.name}</strong> : <i class="fa fa-cog" aria-hidden></i>}
</button>
</div>;
}

View File

@ -275,7 +275,7 @@ export function PSPanelWrapper(props: {
if (props.scrollable === 'hidden') style.overflow = 'hidden';
return <div
class={'ps-room' + (room.id === '' ? '' : ' ps-room-light') + (props.scrollable === true ? ' scrollable' : '')}
id={`room-${room.id}`}
id={`room-${room.id}`} role="tabpanel" aria-labelledby={`roomtab-${room.id}`}
style={style} onClick={props.focusClick ? PSView.focusIfNoSelection : undefined} onDragEnter={props.onDragEnter}
>
{room.caughtError ? <div class="broadcast broadcast-red"><pre>{room.caughtError}</pre></div> : props.children}
@ -773,7 +773,7 @@ export class PSView extends preact.Component {
if (room.location === 'popup' && room.parentElem) {
return <Panel key={room.id} room={room} />;
}
return <div key={room.id} class="ps-overlay" onClick={this.handleClickOverlay}>
return <div key={room.id} class="ps-overlay" onClick={this.handleClickOverlay} role="dialog">
<Panel room={room} />
</div>;
}

View File

@ -432,34 +432,34 @@ export class BattlePanel extends preact.Component<{ id: string }> {
<p>
{atEnd && this.battle ? (
<button onClick={this.replay} class="button" style={{ width: '5em', marginRight: '3px' }}>
<i class="fa fa-undo"></i><br />Replay
<i class="fa fa-undo" aria-hidden></i><br />Replay
</button>
) : !this.battle || this.battle.paused ? (
<button onClick={this.play} class="button" disabled={!this.battle} style={{ width: '5em', marginRight: '3px' }}>
<i class="fa fa-play"></i><br /><strong>Play</strong>
<i class="fa fa-play" aria-hidden></i><br /><strong>Play</strong>
</button>
) : (
<button onClick={this.pause} class="button" style={{ width: '5em', marginRight: '3px' }}>
<i class="fa fa-pause"></i><br /><strong>Pause</strong>
<i class="fa fa-pause" aria-hidden></i><br /><strong>Pause</strong>
</button>
)} {}
<button class="button button-first" disabled={atStart} onClick={this.firstTurn}>
<i class="fa fa-fast-backward"></i><br />First turn
<i class="fa fa-fast-backward" aria-hidden></i><br />First turn
</button>
<button
class="button button-first" disabled={atStart} style={{ marginLeft: '1px', position: 'relative', zIndex: '1' }}
onClick={this.prevTurn}
>
<i class="fa fa-step-backward"></i><br />Prev turn
<i class="fa fa-step-backward" aria-hidden></i><br />Prev turn
</button>
<button class="button button-last" disabled={atEnd} style={{ marginRight: '2px' }} onClick={this.nextTurn}>
<i class="fa fa-step-forward"></i><br />Skip turn
<i class="fa fa-step-forward" aria-hidden></i><br />Skip turn
</button>
<button class="button button-last" disabled={atEnd} onClick={this.lastTurn}>
<i class="fa fa-fast-forward"></i><br />Skip to end
<i class="fa fa-fast-forward" aria-hidden></i><br />Skip to end
</button> {}
<button class="button" onClick={this.openTurn}>
<i class="fa fa-repeat"></i> Go to turn...
<i class="fa fa-repeat" aria-hidden></i> Go to turn...
</button>
</p>
<p>
@ -496,7 +496,7 @@ export class BattlePanel extends preact.Component<{ id: string }> {
Viewpoint:<br />
<button onClick={this.switchViewpoint} name="viewpoint" class={this.battle ? 'button' : 'button disabled'}>
{(this.battle?.viewpointSwitched ? this.result?.players[1] : this.result?.players[0] || "Player")} {}
<i class="fa fa-random" aria-label="Switch viewpoint"></i>
<i class="fa fa-random" aria-hidden aria-label="Switch viewpoint"></i>
</button>
</label> {}
<label class="optgroup">
@ -518,7 +518,7 @@ export class BattlePanel extends preact.Component<{ id: string }> {
{/* {} <code>{this.keyCode}</code> */}
</p> : <p>&nbsp;</p>}
{!PSRouter.showingLeft() && <p>
<a href={PSRouter.href(PSRouter.leftLoc)} class="button"><i class="fa fa-caret-left"></i> More replays</a>
<a href={PSRouter.href(PSRouter.leftLoc)} class="button"><i class="fa fa-caret-left" aria-hidden></i> More replays</a>
</p>}
</div>;
}

View File

@ -29,7 +29,7 @@ function ReplayLink(props: {
return <a href={PSRouter.href(url)} class="blocklink">
<small>{replay.format}{replay.rating ? ` (Rating: ${replay.rating})` : ''}<br /></small>
{!!replay.private && <i class="fa fa-lock"></i>} {}
{!!replay.private && <i class="fa fa-lock" aria-hidden></i>} {}
<strong>{replay.players[0]}</strong> vs. <strong>{replay.players[1]}</strong>
{props.children && <small><br />
{props.children}
@ -241,11 +241,15 @@ export class SearchPanel extends preact.Component<{ id: string }> {
</a>
</p>}
{activelySearching && this.page > 1 && <p class="pagelink">
<a href={this.modLink({ page: -1 })} class="button"><i class="fa fa-caret-up"></i><br />Page {this.page - 1}</a>
<a href={this.modLink({ page: -1 })} class="button">
<i class="fa fa-caret-up" aria-hidden></i><br />Page {this.page - 1}
</a>
</p>}
{activelySearching && searchResults}
{activelySearching && (this.results?.length || 0) > 50 && <p class="pagelink">
<a href={this.modLink({ page: 1 })} class="button">Page {this.page + 1}<br /><i class="fa fa-caret-down"></i></a>
<a href={this.modLink({ page: 1 })} class="button">
Page {this.page + 1}<br /><i class="fa fa-caret-down" aria-hidden></i>
</a>
</p>}
</form>
</section>