Skip to content

Commit

Permalink
[TASK] Improve accessibility of login
Browse files Browse the repository at this point in the history
Colour contrast for footnote text meets WCAG 2.1 requirements.
Role status for the caps-lock notice belongs to the div
wrapping the image.
The HTML document has a heading structure (h1 to h3).
The content is placed in ARIA regions.
The first form field has the autofocus (interface selector comes
after password).
All password fields have a caps lock status notification.
The copyright notice implements the WAI-ARIA 1.1 disclosure pattern.
The message after successful changing the password contains
an accessible link.

Resolves: #92904
Releases: master
Change-Id: I67a2e61644711d20a20432b3eed4dca808ec591b
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/66818
Tested-by: Richard Haeser <richard@richardhaeser.com>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Richard Haeser <richard@richardhaeser.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
  • Loading branch information
Martin Kutschker authored and lolli42 committed Jan 7, 2021
1 parent e9daa05 commit 210b2c0
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 45 deletions.
11 changes: 5 additions & 6 deletions Build/Sources/Sass/typo3/_login_screen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ $login-input-padding-horizontal: $padding-large-horizontal;
.typo3-login-footnote {
margin-left: auto;
margin-right: auto;
font-size: 0.9em;
font-size: 0.95em;
text-align: center;
padding: 1em 1.5em;
display: block;
color: #999;
color: #666;

@media (min-width: $screen-sm-min) {
flex: none;
Expand Down Expand Up @@ -114,7 +114,6 @@ $login-input-padding-horizontal: $padding-large-horizontal;
//
.typo3-login-logo {
padding-top: 1em;
margin-bottom: 2.5em;

img {
display: block;
Expand All @@ -141,13 +140,13 @@ $login-input-padding-horizontal: $padding-large-horizontal;
border-radius: $login-border-radius;

.card-heading {
padding: 1.5em 2.5em;
padding: 2.5em 2.5em 0;
border-top-left-radius: $login-border-radius - 1;
border-top-right-radius: $login-border-radius - 1;
}

.card-body {
padding: 2.5em;
padding: 1.75em 2.5em 2.5em;
border-bottom: 3px solid $login-highlight;
}

Expand Down Expand Up @@ -254,7 +253,7 @@ $login-input-padding-horizontal: $padding-large-horizontal;
}

.typo3-login-copyright-text {
font-size: 0.9em;
font-size: 0.95em;
margin-top: $line-height-computed;
color: $login-copyright-text;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,19 @@ class UserPassLogin {
this.options = {
passwordField: '.t3js-login-password-field',
usernameField: '.t3js-login-username-field',
copyrightLink: 't3js-login-copyright-link',
};

// register submit handler
Login.options.submitHandler = this.resetPassword;

const $usernameField = $(this.options.usernameField);
const $passwordField = $(this.options.passwordField);
const copyrightLink = document.getElementsByClassName(this.options.copyrightLink)[0];

$usernameField.on('keypress', this.showCapsLockWarning);
$passwordField.on('keypress', this.showCapsLockWarning);
copyrightLink.addEventListener('keydown', this.toggleCopyright);

// if the login screen is shown in the login_frameset window for re-login,
// then try to get the username of the current/former login from opening windows main frame:
Expand Down Expand Up @@ -108,6 +111,12 @@ class UserPassLogin {
.find('.t3js-login-alert-capslock')
.toggleClass('hidden', !UserPassLogin.isCapslockEnabled(event));
}

public toggleCopyright = (event: KeyboardEvent): void => {
if (event.key === ' ') {
(<HTMLLinkElement>(event.target)).click();
}
}
}

export = new UserPassLogin();
15 changes: 15 additions & 0 deletions typo3/sysext/backend/Resources/Private/Language/locallang.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ Have a nice day.</source>
<trans-unit id="foldertreeview.noFolders.message" resname="foldertreeview.noFolders.message">
<source>You do not have access to any folder. Please ask your administrator to fix access permissions for your account.</source>
</trans-unit>
<trans-unit id="login.header" resname="login.header">
<source>Login</source>
</trans-unit>
<trans-unit id="login.region.footnote" resname="login.region.footnote">
<source>Footnote</source>
</trans-unit>
<trans-unit id="login.link" resname="login.link">
<source>Login with username and password</source>
</trans-unit>
Expand All @@ -64,6 +70,9 @@ Have a nice day.</source>
<trans-unit id="login.error.capslock" resname="login.error.capslock">
<source>Attention: Caps lock enabled!</source>
</trans-unit>
<trans-unit id="login.error.capslock" resname="login.error.capslockStatus">
<source>Caps lock enabled</source>
</trans-unit>
<trans-unit id="login.interface" resname="login.interface">
<source>Interface</source>
</trans-unit>
Expand All @@ -73,6 +82,12 @@ Have a nice day.</source>
<trans-unit id="login.news.next" resname="login.news.next">
<source>Next</source>
</trans-unit>
<trans-unit id="login.navigation.loginProvider" resname="login.navigation.loginProvider">
<source>Login Provider</source>
</trans-unit>
<trans-unit id="login.navigation.typo3" resname="login.navigation.typo3">
<source>Further information about TYPO3</source>
</trans-unit>
<trans-unit id="login.error.message" resname="login.error.message">
<source>Your login attempt did not succeed</source>
</trans-unit>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<source>You've successfully reset your password!</source>
</trans-unit>
<trans-unit id="reset_success.message" resname="reset_success.message">
<source><![CDATA[You can now log in to the TYPO3 backend with your new credentials <a href="%s">here</a>.]]></source>
<source><![CDATA[With your new credentials you can now log in to the <a href="%s">TYPO3 backend</a>.]]></source>
</trans-unit>
<trans-unit id="input.email" resname="input.email">
<source>Enter your email address</source>
Expand Down
64 changes: 37 additions & 27 deletions typo3/sysext/backend/Resources/Private/Layouts/Login.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
<div class="typo3-login-inner">
<div class="typo3-login-container">
<div class="typo3-login-wrap">
<div class="card card-lg card-login">
<div class="card-body">
<div class="card card-login">
<header class="card-heading">
<h1 class="sr-only"><f:translate key="login.header" /></h1>
<div class="typo3-login-logo">
<img src="{logo}" class="typo3-login-image" alt="" />
</div>
</header>
<main class="card-body">
<f:if condition="{formType} == 'LoginForm'">
<f:then>
<f:if condition="{hasLoginError}">
Expand All @@ -34,29 +37,32 @@
<f:form.hidden name="redirect_url" value="{redirectUrl}" />
<f:form.hidden name="loginRefresh" value="{loginRefresh}" />

<f:render partial="Login/InterfaceSelector" arguments="{_all}" />

<f:render section="loginFormFields" />

<f:render partial="Login/InterfaceSelector" arguments="{_all}" />

<div class="form-group" id="t3-login-submit-section">
<button class="btn btn-block btn-login t3js-login-submit" id="t3-login-submit" type="submit" name="commandLI" data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> {f:translate(key: 'login.process')}" autocomplete="off">
<f:translate key="login.submit" />
</button>
</div>
<ul class="list-unstyled typo3-login-links">
<f:for each="{loginProviders}" as="provider" key="providerKey">
<f:if condition="{provider.label}">
<f:if condition="{loginProviderIdentifier} != {providerKey}">
<li class="t3js-loginprovider-switch" data-providerkey="{providerKey}"><a href="?loginProvider={providerKey}"><i class="fa fa-fw {provider.icon-class}"></i> <span><f:translate key="{provider.label}" /></span></a></li>
</f:if>
</f:if>
</f:for>
</ul>
<f:if condition="{enablePasswordReset}">
<f:render section="ResetPassword" arguments="{_all}" optional="true" />
</f:if>
</form>
</div>
<nav aria-label="{f:translate(key: 'login.navigation.loginProvider')}">
<f:comment>role is for VoiceOver</f:comment>
<ul class="list-unstyled typo3-login-links" role="list">
<f:for each="{loginProviders}" as="provider" key="providerKey">
<f:if condition="{provider.label}">
<f:if condition="{loginProviderIdentifier} != {providerKey}">
<li class="t3js-loginprovider-switch" data-providerkey="{providerKey}"><a href="?loginProvider={providerKey}"><i class="fa fa-fw {provider.icon-class}"></i> <span><f:translate key="{provider.label}" /></span></a></li>
</f:if>
</f:if>
</f:for>
</ul>
</nav>
<f:if condition="{enablePasswordReset}">
<f:render section="ResetPassword" arguments="{_all}" optional="true" />
</f:if>
</f:then>
<f:else if="{enablePasswordReset}">
<f:render section="ResetPassword" arguments="{_all}" />
Expand Down Expand Up @@ -96,34 +102,38 @@
</form>
</f:else>
</f:if>
</div>
</main>
<f:render partial="LoginNews" arguments="{_all}" />
<div class="card-footer">
<footer class="card-footer">
<div class="typo3-login-copyright-wrap">
<a href="#loginCopyright" class="typo3-login-copyright-link collapsed" data-bs-toggle="collapse" aria-expanded="false" aria-controls="loginCopyright">
<a href="#loginCopyright" class="typo3-login-copyright-link t3js-login-copyright-link collapsed" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="loginCopyright">
<span><f:translate key="login.copyrightLink" /></span>
<img src="{images.typo3}" alt="{f:translate(key: 'login.typo3Logo')}" width="70" height="20" />
<f:comment>the image within the button is only decorative</f:comment>
<img src="{images.typo3}" alt="" width="70" height="20" />
</a>
<div id="loginCopyright" class="collapse">
<div class="typo3-login-copyright-text">
<p>
<f:format.raw>{copyright}</f:format.raw>
</p>
<ul class="list-unstyled">
<li><a href="https://typo3.org" target="_blank" rel="noreferrer" class="t3-login-link-typo3"><i class="fa fa-external-link"></i> TYPO3.org</a></li>
<li><a href="https://typo3.org/donate/online-donation/" target="_blank" rel="noreferrer" class="t3-login-link-donate"><i class="fa fa-external-link"></i> <f:translate key="login.donate" /></a></li>
</ul>
<nav aria-label="{f:translate(key: 'login.navigation.typo3')}">
<f:comment>role is for VoiceOver</f:comment>
<ul class="list-unstyled" role="list">
<li><a href="https://typo3.org" target="_blank" rel="noreferrer" class="t3-login-link-typo3"><i class="fa fa-external-link"></i> TYPO3.org</a></li>
<li><a href="https://typo3.org/donate/online-donation/" target="_blank" rel="noreferrer" class="t3-login-link-donate"><i class="fa fa-external-link"></i> <f:translate key="login.donate" /></a></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</footer>
</div>
</div>
</div>
<f:if condition="{loginFootnote}">
<div class="typo3-login-footnote">
<aside class="typo3-login-footnote" aria-label="{f:translate(key: 'login.region.footnote')}">
<p>{loginFootnote}</p>
</div>
</aside>
</f:if>
</div>
<f:comment>This link is only used for protection of the backend.</f:comment>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="Login" />

<f:section name="ResetPassword">
Expand All @@ -6,7 +7,7 @@ <h2><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset
<f:if condition="{resetInitiated}">
<f:then>
<div class="callout callout-success">
<h4 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:email_sent.headline" /></h4>
<h3 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:email_sent.headline" /></h3>
<div class="callout-body"><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:email_sent.message" arguments="{0: email}" /></div>
</div>
<p class="pull-right">
Expand Down Expand Up @@ -46,3 +47,4 @@ <h4 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/La
</f:if>
</div>
</f:section>
</html>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="Login" />

<f:section name="ResetPassword">
Expand All @@ -14,7 +15,7 @@ <h2><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset
<div class="callout callout-success">
<div class="media">
<div class="media-body">
<h4 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:reset_success.headline" /></h4>
<h3 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:reset_success.headline" /></h3>
<div class="callout-body"><f:format.raw><f:translate key="LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:reset_success.message" arguments="{0: '{f:be.uri(route: \'login\')}'}"/></f:format.raw></div>
</div>
</div>
Expand All @@ -30,14 +31,22 @@ <h4 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/La
<div class="form-group">
<div class="form-control-wrap">
<div class="form-control-holder">
<input type="password" name="password" placeholder="{f:translate(key: 'LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:input.password')}" value="" class="form-control input-login t3js-clearable" autocomplete="off" autofocus="autofocus" required="required" />
<input type="password" name="password" placeholder="{f:translate(key: 'LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:input.password')}" value="" class="form-control input-login t3js-clearable t3js-login-password-field" autocomplete="new-password" autofocus="autofocus" required="required" />
<div role="status" class="form-notice-capslock hidden t3js-login-alert-capslock">
<img aria-hidden="true" src="{images.capslock}" width="14" height="14" alt="" title="{f:translate(key: 'login.error.capslock')}" />
<span class="sr-only"><f:translate key="login.error.capslockStatus" /></span>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="form-control-wrap">
<div class="form-control-holder">
<input type="password" name="passwordrepeat" placeholder="{f:translate(key: 'LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:input.passwordrepeat')}" value="" autocomplete="off" class="form-control input-login t3js-clearable" required="required" />
<input type="password" name="passwordrepeat" placeholder="{f:translate(key: 'LLL:EXT:backend/Resources/Private/Language/locallang_reset_password.xlf:input.passwordrepeat')}" value="" autocomplete="off" class="form-control input-login t3js-clearable t3js-login-password-field" required="required" />
<div role="status" class="form-notice-capslock hidden t3js-login-alert-capslock">
<img aria-hidden="true" src="{images.capslock}" width="14" height="14" alt="" title="{f:translate(key: 'login.error.capslock')}" />
<span class="sr-only"><f:translate key="login.error.capslockStatus" /></span>
</div>
</div>
</div>
</div>
Expand All @@ -51,3 +60,4 @@ <h4 class="callout-title"><f:translate key="LLL:EXT:backend/Resources/Private/La
</f:if>
</div>
</f:section>
</html>
Loading

0 comments on commit 210b2c0

Please sign in to comment.