Improve accessibility for screen reader users
- Marked UI regions with ARIA landmarks - Added text labels to icon-only buttons - Added ARIA state semantics to track star buttons - Added static alt text to cover art images
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@
|
||||
${</if-has-biography>}
|
||||
<div class="btn-group">
|
||||
${play-btn class="btn btn-primary"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" aria-label="${tr:Lms.more}"/>
|
||||
<ul class="dropdown-menu">
|
||||
<li>${play-next class="dropdown-item"}</li>
|
||||
<li>${play-last class="dropdown-item"}</li>
|
||||
|
||||
+2
-2
@@ -3,11 +3,11 @@
|
||||
|
||||
<message id="Lms.main.template">
|
||||
${navbar}
|
||||
<div class="container">
|
||||
<main class="container">
|
||||
${contents}
|
||||
${notifications class="toast-container Lms-notification-container p-3"}
|
||||
${modal}
|
||||
</div>
|
||||
</main>
|
||||
${player class="fixed-bottom bg-dark Lms-player"}
|
||||
</message>
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<message id="Lms.MediaPlayer.template">
|
||||
<audio id="lms-mp-audio">
|
||||
</audio>
|
||||
<div class="container">
|
||||
<div class="container" role="region" aria-label="${tr:Lms.Player.now-playing}">
|
||||
<div class="p-1 my-1 position-relative">
|
||||
<input id="lms-mp-seek" class="Lms-player-seek-common Lms-player-seek" type="range" min="0" max="100" step="1" value="0"/>
|
||||
<input id="lms-mp-seek" class="Lms-player-seek-common Lms-player-seek" type="range" min="0" max="100" step="1" value="0" aria-label="${tr:Lms.Player.seek}"/>
|
||||
<div class="progress Lms-player-seek-common bg-secondary rounded">
|
||||
<div id="lms-mp-progress" class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
|
||||
</div>
|
||||
@@ -15,13 +15,13 @@
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="py-2 pe-2 d-inline-flex">
|
||||
<button id="lms-mp-previous" class="btn btn-sm btn-outline-primary border-0">
|
||||
<button id="lms-mp-previous" class="btn btn-sm btn-outline-primary border-0" aria-label="${tr:Lms.Player.previous}">
|
||||
<i class="fa fa-fw fa-step-backward"/>
|
||||
</button>
|
||||
<button id="lms-mp-playpause" class="btn btn-outline-primary border-0">
|
||||
<button id="lms-mp-playpause" class="btn btn-outline-primary border-0" aria-label="${tr:Lms.Player.play-pause}">
|
||||
<i class="fa fa-fw fa-play"/>
|
||||
</button>
|
||||
<button id="lms-mp-next" class="btn btn-sm btn-outline-primary border-0">
|
||||
<button id="lms-mp-next" class="btn btn-sm btn-outline-primary border-0" aria-label="${tr:Lms.Player.next}">
|
||||
<i class="fa fa-fw fa-step-forward"/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div class="p-2 d-none d-md-flex align-items-center Lms-player-volume-container">
|
||||
<i id="lms-mp-volume" class="fa fa-fw fa-volume-up link-primary me-1"></i>
|
||||
<input id ="lms-mp-volume-slider" class="form-range" type="range" min="0" max="1" step="0.01" value="0.8"/>
|
||||
<input id ="lms-mp-volume-slider" class="form-range" type="range" min="0" max="1" step="0.01" value="0.8" aria-label="${tr:Lms.Player.volume}"/>
|
||||
</div>
|
||||
<div class="py-2 ps-2">
|
||||
${playqueue-btn class="btn btn-outline-primary border-0"}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<message id="Lms.loading">Loading...</message>
|
||||
<message id="Lms.login">Login</message>
|
||||
<message id="Lms.logout"><i class="fa fa-fw fa-sign-out" aria-hidden="true"></i> Logout</message>
|
||||
<message id="Lms.more">More...</message>
|
||||
<message id="Lms.ok">Ok</message>
|
||||
<message id="Lms.password">Password</message>
|
||||
<message id="Lms.password-bad-login-combination">Bad login / password combination</message>
|
||||
@@ -197,6 +198,7 @@
|
||||
<message id="Lms.Explore.all">All</message>
|
||||
<message id="Lms.Explore.artists">Artists</message>
|
||||
<message id="Lms.Explore.bitrate">Bitrate</message>
|
||||
<message id="Lms.Explore.cover-art">Cover art</message>
|
||||
<message id="Lms.Explore.codec">Codec</message>
|
||||
<message id="Lms.Explore.download">Download</message>
|
||||
<message id="Lms.Explore.duration">Duration</message>
|
||||
@@ -207,6 +209,7 @@
|
||||
<message id="Lms.Explore.most-played">Most played</message>
|
||||
<message id="Lms.Explore.musicbrainz-artist">MusicBrainz Artist</message>
|
||||
<message id="Lms.Explore.musicbrainz-release">MusicBrainz Release</message>
|
||||
<message id="Lms.Explore.play-item">Play {1}</message>
|
||||
<message id="Lms.Explore.play">Play</message>
|
||||
<message id="Lms.Explore.play-last">Play last</message>
|
||||
<message id="Lms.Explore.play-next">Play next</message>
|
||||
@@ -221,6 +224,7 @@
|
||||
<message id="Lms.Explore.releases">Albums</message>
|
||||
<message id="Lms.Explore.search">Search</message>
|
||||
<message id="Lms.Explore.star">Star</message>
|
||||
<message id="Lms.Explore.star-item">Star {1}</message>
|
||||
<message id="Lms.Explore.starred">Starred</message>
|
||||
<message id="Lms.Explore.track-info">Track information</message>
|
||||
<message id="Lms.Explore.track-lyrics">Lyrics</message>
|
||||
@@ -309,6 +313,13 @@
|
||||
|
||||
<!--Player-->
|
||||
<message id="Lms.Player.transcoding-active">Transcoding active</message>
|
||||
<message id="Lms.Player.now-playing">Now playing</message>
|
||||
<message id="Lms.Player.previous">Previous</message>
|
||||
<message id="Lms.Player.play-pause">Play / pause</message>
|
||||
<message id="Lms.Player.next">Next</message>
|
||||
<message id="Lms.Player.seek">Seek</message>
|
||||
<message id="Lms.Player.volume">Volume</message>
|
||||
|
||||
|
||||
<!--Playqueue-->
|
||||
<message id="Lms.PlayQueue.clear">Clear</message>
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
${clusters class="mb-2"}
|
||||
<div class="btn-group">
|
||||
${play-btn class="btn btn-primary"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" aria-label="${tr:Lms.more}"/>
|
||||
<ul class="dropdown-menu">
|
||||
<li>${play-next class="dropdown-item"}</li>
|
||||
<li>${play-last class="dropdown-item"}</li>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div class="btn-group me-1">
|
||||
${play-btn class="btn btn-primary"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" aria-label="${tr:Lms.more}"/>
|
||||
<ul class="dropdown-menu">
|
||||
<li>${play-next class="dropdown-item"}</li>
|
||||
<li>${play-last class="dropdown-item"}</li>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div class="btn-group me-1">
|
||||
${play-btn class="btn btn-primary"}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"/>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false" aria-label="${tr:Lms.more}"/>
|
||||
<ul class="dropdown-menu">
|
||||
<li>${play-next class="dropdown-item"}</li>
|
||||
<li>${play-last class="dropdown-item"}</li>
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace lms::ui::utils
|
||||
auto image{ std::make_unique<Wt::WImage>() };
|
||||
image->setStyleClass("Lms-cover img-fluid"); // HACK
|
||||
image->setAttributeValue("onload", LmsApp->javaScriptClass() + ".onLoadCover(this)"); // HACK
|
||||
image->setAlternateText(Wt::WString::tr("Lms.Explore.cover-art"));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -475,12 +475,14 @@ namespace lms::ui
|
||||
}
|
||||
|
||||
Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
|
||||
playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.play-item").arg(track->getName()));
|
||||
playBtn->clicked().connect([this, trackId] {
|
||||
_playQueueController.playTrackInRelease(trackId);
|
||||
});
|
||||
|
||||
{
|
||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML)
|
||||
->setAttributeValue("aria-label", Wt::WString::tr("Lms.more"));
|
||||
entry->bindNew<Wt::WPushButton>("play", Wt::WString::tr("Lms.Explore.play"))
|
||||
->clicked()
|
||||
.connect([this, trackId] {
|
||||
@@ -501,6 +503,8 @@ namespace lms::ui
|
||||
auto isStarred{ [=] { return core::Service<feedback::IFeedbackService>::get()->isStarred(LmsApp->getUserId(), trackId); } };
|
||||
|
||||
Wt::WPushButton* starBtn{ entry->bindNew<Wt::WPushButton>("star-btn", Wt::WString::tr(isStarred() ? "Lms.template.unstar-btn" : "Lms.template.star-btn"), Wt::TextFormat::XHTML) };
|
||||
starBtn->setAttributeValue("aria-pressed", isStarred() ? "true" : "false");
|
||||
starBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.star-item").arg(track->getName()));
|
||||
Wt::WPushButton* starMenuEntry{ entry->bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) };
|
||||
|
||||
auto toggle{ [=] {
|
||||
@@ -511,12 +515,14 @@ namespace lms::ui
|
||||
core::Service<feedback::IFeedbackService>::get()->unstar(LmsApp->getUserId(), trackId);
|
||||
starMenuEntry->setText(Wt::WString::tr("Lms.Explore.star"));
|
||||
starBtn->setText(Wt::WString::tr("Lms.template.star-btn"));
|
||||
starBtn->setAttributeValue("aria-pressed", "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
core::Service<feedback::IFeedbackService>::get()->star(LmsApp->getUserId(), trackId);
|
||||
starMenuEntry->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
||||
starBtn->setText(Wt::WString::tr("Lms.template.unstar-btn"));
|
||||
starBtn->setAttributeValue("aria-pressed", "true");
|
||||
}
|
||||
} };
|
||||
|
||||
@@ -648,6 +654,7 @@ namespace lms::ui
|
||||
|
||||
{
|
||||
const db::MediumId mediumId{ medium->getId() };
|
||||
Wt::WString disc_title;
|
||||
|
||||
std::unique_ptr<Wt::WTemplate> disc{ std::make_unique<Template>(Wt::WString::tr("Lms.Explore.Release.template.entry-disc")) };
|
||||
disc->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||
@@ -666,15 +673,18 @@ namespace lms::ui
|
||||
}
|
||||
|
||||
if (medium->getName().empty())
|
||||
disc->bindNew<Wt::WText>("disc-title", Wt::WString::tr("Lms.Explore.Release.disc").arg(medium->getPosition() ? *medium->getPosition() : 1 /* TODO */));
|
||||
disc_title = Wt::WString::tr("Lms.Explore.Release.disc").arg(medium->getPosition() ? *medium->getPosition() : 1 /* TODO */);
|
||||
else
|
||||
disc->bindString("disc-title", Wt::WString::fromUTF8(std::string{ medium->getName() }), Wt::TextFormat::Plain);
|
||||
|
||||
disc_title = Wt::WString::fromUTF8(std::string{ medium->getName() });
|
||||
disc->bindNew<Wt::WText>("disc-title", disc_title, Wt::TextFormat::Plain);
|
||||
Wt::WPushButton* playBtn{ disc->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
|
||||
playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.play-item").arg(disc_title));
|
||||
playBtn->clicked().connect([this, mediumId] {
|
||||
_playQueueController.processCommand(PlayQueueController::Command::Play, mediumId);
|
||||
});
|
||||
disc->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
||||
disc->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML)
|
||||
->setAttributeValue("aria-label", Wt::WString::tr("Lms.more"));
|
||||
;
|
||||
disc->bindNew<Wt::WPushButton>("play", Wt::WString::tr("Lms.Explore.play"))
|
||||
->clicked()
|
||||
.connect([this, mediumId] {
|
||||
|
||||
@@ -250,11 +250,13 @@ namespace lms::ui::TrackListHelpers
|
||||
entry->bindString("duration", utils::durationToString(track->getDuration()), Wt::TextFormat::Plain);
|
||||
|
||||
Wt::WPushButton* playBtn{ entry->bindNew<Wt::WPushButton>("play-btn", Wt::WString::tr("Lms.template.play-btn"), Wt::TextFormat::XHTML) };
|
||||
playBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.play-item").arg(track->getName()));
|
||||
playBtn->clicked().connect([trackId, &playQueueController] {
|
||||
playQueueController.processCommand(PlayQueueController::Command::Play, { trackId });
|
||||
});
|
||||
|
||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML);
|
||||
entry->bindNew<Wt::WPushButton>("more-btn", Wt::WString::tr("Lms.template.more-btn"), Wt::TextFormat::XHTML)
|
||||
->setAttributeValue("aria-label", Wt::WString::tr("Lms.more"));
|
||||
|
||||
entry->bindNew<Wt::WPushButton>("play", Wt::WString::tr("Lms.Explore.play"))
|
||||
->clicked()
|
||||
@@ -276,6 +278,8 @@ namespace lms::ui::TrackListHelpers
|
||||
auto isStarred{ [=] { return core::Service<feedback::IFeedbackService>::get()->isStarred(LmsApp->getUserId(), trackId); } };
|
||||
|
||||
Wt::WPushButton* starBtn{ entry->bindNew<Wt::WPushButton>("star-btn", Wt::WString::tr(isStarred() ? "Lms.template.unstar-btn" : "Lms.template.star-btn"), Wt::TextFormat::XHTML) };
|
||||
starBtn->setAttributeValue("aria-label", Wt::WString::tr("Lms.Explore.star-item").arg(track->getName()));
|
||||
starBtn->setAttributeValue("aria-pressed", isStarred() ? "true" : "false");
|
||||
Wt::WPushButton* starMenuEntry{ entry->bindNew<Wt::WPushButton>("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) };
|
||||
|
||||
auto toggle{ [=] {
|
||||
@@ -286,12 +290,14 @@ namespace lms::ui::TrackListHelpers
|
||||
core::Service<feedback::IFeedbackService>::get()->unstar(LmsApp->getUserId(), trackId);
|
||||
starMenuEntry->setText(Wt::WString::tr("Lms.Explore.star"));
|
||||
starBtn->setText(Wt::WString::tr("Lms.template.star-btn"));
|
||||
starBtn->setAttributeValue("aria-pressed", "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
core::Service<feedback::IFeedbackService>::get()->star(LmsApp->getUserId(), trackId);
|
||||
starMenuEntry->setText(Wt::WString::tr("Lms.Explore.unstar"));
|
||||
starBtn->setText(Wt::WString::tr("Lms.template.unstar-btn"));
|
||||
starBtn->setAttributeValue("aria-pressed", "true");
|
||||
}
|
||||
} };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user