Skip to content

Commit 77b4323

Browse files
committed
feat(ui): a11y, autofill, and i18n pass on auth screens
- SignInForm/RegisterForm: real labels, autoComplete, semantic password toggle button, inline error messages, password complexity message, password==confirmPassword check, fix register submit label - Login/Register pages: i18n hardcoded copy, mobile card chrome, buttons replace anchor onClicks, register back button to login - Passkeys (login + register) and Password Reset: same card/i18n treatment, back button, success state, autoComplete - Strip welcome emojis from Home/TOTP/LoginExternal headers - Add locale keys for new UI strings (en + de)
1 parent 31378c0 commit 77b4323

13 files changed

Lines changed: 847 additions & 287 deletions

File tree

locales/de/common.json

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,51 @@
11
{
2-
"WELCOME": "👋 Welcome!",
3-
"WELCOME_WITH_ORG_DISPLAY_NAME": "👋 Welcome to {{displayName}}",
4-
"SWITCH_REGISTER_PAGE": "Don't have an account?",
5-
"REGISTER_NOW": "Register now",
6-
"LOG_IN": "Login",
7-
"SOMETHING_WENT_WRONG": "Something went wrong"
2+
"WELCOME": "Willkommen!",
3+
"WELCOME_WITH_ORG_DISPLAY_NAME": "Willkommen bei {{displayName}}",
4+
"REGISTER_TITLE": "Registrieren",
5+
"BACK": "Zurück",
6+
"SWITCH_REGISTER_PAGE": "Noch kein Konto?",
7+
"REGISTER_NOW": "Jetzt registrieren",
8+
"REGISTER": "Registrieren",
9+
"LOG_IN": "Anmelden",
10+
"LOADING": "Lädt…",
11+
"USERNAME": "Benutzername",
12+
"USERNAME_PLACEHOLDER": "du@beispiel.de",
13+
"EMAIL": "E-Mail",
14+
"EMAIL_PLACEHOLDER": "du@beispiel.de",
15+
"EMAIL_INVALID": "Bitte eine gültige E-Mail-Adresse eingeben.",
16+
"GIVEN_NAME": "Vorname",
17+
"GIVEN_NAME_PLACEHOLDER": "Max",
18+
"FAMILY_NAME": "Nachname",
19+
"FAMILY_NAME_PLACEHOLDER": "Mustermann",
20+
"PASSWORD": "Passwort",
21+
"PASSWORD_PLACEHOLDER": "Passwort eingeben",
22+
"CONFIRM_PASSWORD": "Passwort bestätigen",
23+
"CONFIRM_PASSWORD_PLACEHOLDER": "Passwort erneut eingeben",
24+
"PASSWORD_CONFIRM_MISMATCH": "Die Passwörter stimmen nicht überein.",
25+
"SHOW_PASSWORD": "Passwort anzeigen",
26+
"HIDE_PASSWORD": "Passwort ausblenden",
27+
"REQUIRED": "Pflichtfeld",
28+
"USERNAME_INVALID": "Benutzername darf keine Leerzeichen enthalten.",
29+
"PASSWORD_COMPLEXITY": "Passwort muss mindestens 8 Zeichen enthalten, davon Groß- und Kleinbuchstaben, eine Ziffer und ein Sonderzeichen.",
30+
"ERROR_PASSWORD_LIMIT_SIZE": "Passwort darf höchstens {{maxByteSize}} Bytes lang sein.",
31+
"FORGOT_PASSWORD": "Passwort vergessen?",
32+
"PASSWORD_RESET_TITLE": "Passwort zurücksetzen",
33+
"PASSWORD_RESET_HINT": "Benutzername eingeben — wir schicken dir per E-Mail einen Link zum Zurücksetzen.",
34+
"SEND_RESET_LINK": "Link zum Zurücksetzen senden",
35+
"PASSWORD_RESET_SENT": "E-Mail verschickt",
36+
"PASSWORD_RESET_SENT_HINT": "Bitte den Posteingang prüfen — es kann einen Moment dauern.",
37+
"BACK_TO_LOGIN": "Zurück zur Anmeldung",
38+
"LOGIN_WITH_PASSKEYS": "Mit Passkeys anmelden",
39+
"LOG_IN_WITH_PASSKEY": "Mit Passkey anmelden",
40+
"PASSKEY_LOGIN_TITLE": "Mit Passkey anmelden",
41+
"PASSKEY_LOGIN_HINT": "Benutzername eingeben und mit dem Passkey deines Geräts anmelden.",
42+
"PASSKEY_REGISTER_TITLE": "Passkey einrichten",
43+
"PASSKEY_REGISTER_HINT": "Nutze die Biometrie oder Bildschirmsperre deines Geräts für eine schnellere Anmeldung.",
44+
"REGISTER_PASSKEY": "Passkey registrieren",
45+
"SIGNED_IN_AS": "Angemeldet als",
46+
"OR_CONTINUE_WITH": "oder weiter mit",
47+
"LOGGING_IN_TO_APP": "Du meldest dich bei {{appName}} an",
48+
"LOGIN_ERROR": "Anmeldung fehlgeschlagen. Bitte Anmeldedaten prüfen.",
49+
"REGISTER_ERROR": "Konto konnte nicht erstellt werden. Bitte erneut versuchen.",
50+
"SOMETHING_WENT_WRONG": "Etwas ist schiefgelaufen"
851
}

locales/en/common.json

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,51 @@
11
{
2-
"WELCOME": "👋 Welcome!",
3-
"WELCOME_WITH_ORG_DISPLAY_NAME": "👋 Welcome to {{displayName}}",
2+
"WELCOME": "Welcome!",
3+
"WELCOME_WITH_ORG_DISPLAY_NAME": "Welcome to {{displayName}}",
4+
"REGISTER_TITLE": "Register",
5+
"BACK": "Back",
46
"SWITCH_REGISTER_PAGE": "Don't have an account?",
57
"REGISTER_NOW": "Register now",
6-
"LOG_IN": "Login",
8+
"REGISTER": "Register",
9+
"LOG_IN": "Log in",
10+
"LOADING": "Loading…",
11+
"USERNAME": "Username",
12+
"USERNAME_PLACEHOLDER": "you@example.com",
13+
"EMAIL": "Email",
14+
"EMAIL_PLACEHOLDER": "you@example.com",
15+
"EMAIL_INVALID": "Please enter a valid email address.",
16+
"GIVEN_NAME": "First name",
17+
"GIVEN_NAME_PLACEHOLDER": "Jane",
18+
"FAMILY_NAME": "Last name",
19+
"FAMILY_NAME_PLACEHOLDER": "Doe",
20+
"PASSWORD": "Password",
21+
"PASSWORD_PLACEHOLDER": "Enter your password",
22+
"CONFIRM_PASSWORD": "Confirm password",
23+
"CONFIRM_PASSWORD_PLACEHOLDER": "Re-enter your password",
24+
"PASSWORD_CONFIRM_MISMATCH": "Passwords don't match.",
25+
"SHOW_PASSWORD": "Show password",
26+
"HIDE_PASSWORD": "Hide password",
27+
"REQUIRED": "Required",
28+
"USERNAME_INVALID": "Username can't contain spaces.",
29+
"PASSWORD_COMPLEXITY": "Password must be at least 8 characters and include uppercase, lowercase, a number, and a special character.",
30+
"ERROR_PASSWORD_LIMIT_SIZE": "Password must be at most {{maxByteSize}} bytes.",
31+
"FORGOT_PASSWORD": "Forgot password?",
32+
"PASSWORD_RESET_TITLE": "Reset password",
33+
"PASSWORD_RESET_HINT": "Enter your username and we'll email you a link to reset your password.",
34+
"SEND_RESET_LINK": "Send reset link",
35+
"PASSWORD_RESET_SENT": "Email sent",
36+
"PASSWORD_RESET_SENT_HINT": "Check your inbox for the reset link. It may take a minute to arrive.",
37+
"BACK_TO_LOGIN": "Back to login",
38+
"LOGIN_WITH_PASSKEYS": "Log in with passkeys",
39+
"LOG_IN_WITH_PASSKEY": "Log in with passkey",
40+
"PASSKEY_LOGIN_TITLE": "Log in with passkey",
41+
"PASSKEY_LOGIN_HINT": "Enter your username and use your device's passkey to sign in.",
42+
"PASSKEY_REGISTER_TITLE": "Set up a passkey",
43+
"PASSKEY_REGISTER_HINT": "Use your device's biometrics or screen lock for faster sign-in next time.",
44+
"REGISTER_PASSKEY": "Register passkey",
45+
"SIGNED_IN_AS": "Signed in as",
46+
"OR_CONTINUE_WITH": "or continue with",
47+
"LOGGING_IN_TO_APP": "You are logging in to {{appName}}",
48+
"LOGIN_ERROR": "Couldn't sign you in. Check your credentials and try again.",
49+
"REGISTER_ERROR": "Couldn't create your account. Please try again.",
750
"SOMETHING_WENT_WRONG": "Something went wrong"
851
}

ui/Home/Home.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default (props: {
4444

4545
<div className="flex flex-grow items-center justify-center">
4646
<div className="flex w-[480px] lg:p-[40px] flex-col items-center justify-center rounded-[8px] border-gray-300 md:border">
47-
<h1 className="text-[42px]">👋 Welcome!</h1>
47+
<h1 className="text-[42px]">Welcome!</h1>
4848

4949
{/* <pre className="hidden">{JSON.stringify(activeSession, null, 2)}</pre> */}
5050

ui/Login/Login.tsx

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const LoginPage: React.FC<{
9898
console.error(error);
9999

100100
toastRef.current?.show({
101-
message: 'Login error',
101+
message: t('LOGIN_ERROR'),
102102
intent: 'error',
103103
});
104104

@@ -132,13 +132,13 @@ const LoginPage: React.FC<{
132132
}
133133

134134
return (
135-
<div className="flex h-full w-full flex-col items-center justify-center">
135+
<div className="flex h-full w-full flex-col items-center justify-center px-4 py-8 sm:py-12">
136136
<LoadingState loading={isLoading} />
137137

138-
<div className="flex w-full flex-col justify-center rounded-md border-gray-300 lg:w-[480px] lg:border p-5">
139-
<h3 className="my-6 text-center text-3xl font-extrabold text-gray-900">
140-
👋 Welcome!
141-
</h3>
138+
<div className="w-full max-w-[440px] rounded-lg border border-gray-200 bg-white p-6 shadow-sm sm:p-8">
139+
<h1 className="mb-6 text-center text-2xl font-bold text-gray-900 sm:text-3xl">
140+
{t('WELCOME')}
141+
</h1>
142142

143143
<SignInForm
144144
loading={isLoading}
@@ -148,8 +148,9 @@ const LoginPage: React.FC<{
148148

149149
{(loginSettings?.passkeysType as unknown as string) ===
150150
'PASSKEYS_TYPE_ALLOWED' && (
151-
<a
152-
className="flex self-center cursor-pointer text-[12px] font-normal text-info mb-2 mt-2"
151+
<button
152+
type="button"
153+
className="mt-4 block w-full text-center text-xs font-medium text-info hover:underline focus:outline-none focus:underline"
153154
onClick={() => {
154155
setIsLoading(true);
155156

@@ -160,15 +161,16 @@ const LoginPage: React.FC<{
160161
);
161162
}}
162163
>
163-
Login with passkeys
164-
</a>
164+
{t('LOGIN_WITH_PASSKEYS')}
165+
</button>
165166
)}
166167

167-
<div className="flex justify-between items-center">
168+
<div className="mt-4 flex flex-wrap items-center justify-between gap-2">
168169
<div>
169170
{!loginSettings?.hidePasswordReset && (
170-
<a
171-
className="text-[12px] font-normal text-[#4F6679] cursor-pointer"
171+
<button
172+
type="button"
173+
className="text-xs font-medium text-gray700 hover:underline focus:outline-none focus:underline"
172174
onClick={() => {
173175
setIsLoading(true);
174176

@@ -179,48 +181,54 @@ const LoginPage: React.FC<{
179181
);
180182
}}
181183
>
182-
Forgot password?
183-
</a>
184+
{t('FORGOT_PASSWORD')}
185+
</button>
184186
)}
185187
</div>
186188

187189
{loginSettings?.allowRegister && (
188-
<p className="text-center text-black font-normal">
189-
<Link
190-
className="text-info text-[15px] font-normal"
191-
data-testid={'registerSwitch'}
192-
onClick={() => setIsLoading(true)}
193-
href={objectToQueryString(ROUTING.REGISTER, {
194-
authRequest: authRequest?.id,
195-
})}
196-
>
197-
{t('REGISTER_NOW')}
198-
</Link>
199-
</p>
190+
<Link
191+
className="text-sm font-medium text-info hover:underline focus:outline-none focus:underline"
192+
data-testid={'registerSwitch'}
193+
onClick={() => setIsLoading(true)}
194+
href={objectToQueryString(ROUTING.REGISTER, {
195+
authRequest: authRequest?.id,
196+
})}
197+
>
198+
{t('REGISTER_NOW')}
199+
</Link>
200200
)}
201201
</div>
202202

203203
{!!identityProviders?.length && (
204-
<div className="w-full">
205-
{identityProviders.map((e) => (
206-
<button
207-
type="submit"
208-
className="disabled:bg-gray-300 group w-full flex justify-center py-2 border text-sm font-medium rounded-md border-black my-5"
209-
onClick={() => startExternal(e.id)}
210-
disabled={isLoading}
211-
>
212-
{e.name}
213-
</button>
214-
))}
215-
</div>
204+
<>
205+
<div className="my-6 flex items-center gap-3">
206+
<div className="h-px flex-1 bg-gray-200" />
207+
<span className="text-xs uppercase tracking-wider text-gray-500">
208+
{t('OR_CONTINUE_WITH')}
209+
</span>
210+
<div className="h-px flex-1 bg-gray-200" />
211+
</div>
212+
<div className="flex flex-col gap-2">
213+
{identityProviders.map((e) => (
214+
<button
215+
key={e.id}
216+
type="button"
217+
className="flex w-full items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
218+
onClick={() => startExternal(e.id)}
219+
disabled={isLoading}
220+
>
221+
{e.name}
222+
</button>
223+
))}
224+
</div>
225+
</>
216226
)}
217-
218-
{/* <pre className="hidden">{JSON.stringify(loginSettings, null, 2)}</pre> */}
219227
</div>
220228

221229
{application?.name && (
222-
<p className="mb-[18px] text-[12px] font-normal text-[#4F6679]">
223-
You are logging in to {application?.name}
230+
<p className="mt-4 text-xs text-gray-500">
231+
{t('LOGGING_IN_TO_APP', { appName: application.name })}
224232
</p>
225233
)}
226234
<Toast ref={toastRef} />

0 commit comments

Comments
 (0)