CIAM registration form buttons, inputs and fixes (#29740)

* Many fixes to CIAM registration form, including Phosphor icons

* Unify layout between Pug and React, fixes for spacing and mobile screen sizes

* Pug lint fix

* Make CIAM footer links underlined

* Add CIAM error notification styling

* Merge duplicate style rules

* Remove outdated comment

* Fix ordering of en.json

* Move aria-label to buttons

* Move full stop into translation string

* Remove dummy password strength indicator

* CIAM spacing and label fixes

* Header logo fixes from review

* Add aria-hidden to error icon

GitOrigin-RevId: 87c8181566f0878256b8010f95f115ec25c7ceb9
This commit is contained in:
Tim Down
2025-11-21 12:55:04 +00:00
committed by Copybot
parent f2a05b1a2e
commit 00f6a1e0f9
13 changed files with 274 additions and 80 deletions
+51 -3
View File
@@ -13191,7 +13191,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz",
"integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@lit/reactive-element": {
@@ -15620,6 +15619,55 @@
"react-dom": ">= 16.8"
}
},
"node_modules/@phosphor-icons/webcomponents": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz",
"integrity": "sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==",
"license": "MIT",
"dependencies": {
"lit": "^3"
}
},
"node_modules/@phosphor-icons/webcomponents/node_modules/@lit/reactive-element": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz",
"integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==",
"license": "BSD-3-Clause",
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.4.0"
}
},
"node_modules/@phosphor-icons/webcomponents/node_modules/lit": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz",
"integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==",
"license": "BSD-3-Clause",
"dependencies": {
"@lit/reactive-element": "^2.1.0",
"lit-element": "^4.2.0",
"lit-html": "^3.3.0"
}
},
"node_modules/@phosphor-icons/webcomponents/node_modules/lit-element": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz",
"integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==",
"license": "BSD-3-Clause",
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.4.0",
"@lit/reactive-element": "^2.1.0",
"lit-html": "^3.3.0"
}
},
"node_modules/@phosphor-icons/webcomponents/node_modules/lit-html": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz",
"integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==",
"license": "BSD-3-Clause",
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -20579,8 +20627,7 @@
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
},
"node_modules/@types/unist": {
"version": "2.0.11",
@@ -55665,6 +55712,7 @@
"@overleaf/stream-utils": "*",
"@overleaf/validation-tools": "*",
"@phosphor-icons/react": "^2.1.7",
"@phosphor-icons/webcomponents": "^2.1.5",
"@slack/webhook": "^7.0.2",
"@stripe/react-stripe-js": "^3.9.0",
"@stripe/stripe-js": "^7.7.0",
+17 -2
View File
@@ -2,13 +2,15 @@ include terms_of_service
include recaptcha
mixin ciamLogo
a.brand.overleaf-ds-logo(href='/' aria-label='Overleaf')
header.ciam-logo
a.brand.overleaf-ds-logo(href='/')
span.visually-hidden Overleaf
mixin ciamCardSeparator
hr.ciam-card-separator
mixin ciamCardFooter
.ciam-card-footer
section.ciam-card-footer
+ciamCardSeparator
.ciam-footer-ds-logo
img(
@@ -25,3 +27,16 @@ mixin ciamTermsOfServiceAgreement
mixin ciamRecaptchaConditions
p
+recaptchaConditionsContent
mixin ciamCustomFormDangerMessage(key)
div(
class='notification ciam-notification notification-type-error'
hidden
data-ol-custom-form-message=key
role='alert'
aria-live='polite'
)
.notification-icon
ph-warning-circle(aria-hidden='true')
.notification-content.text-left
block
@@ -0,0 +1,4 @@
// These are used in the CIAM registration form
import '@phosphor-icons/webcomponents/PhBank'
import '@phosphor-icons/webcomponents/PhEye'
import '@phosphor-icons/webcomponents/PhEyeSlash'
@@ -1,24 +1,42 @@
import { materialIcon } from '@/features/utils/material-icon'
import classNames from 'classnames'
import '@phosphor-icons/webcomponents/PhWarningCircle'
function dsErrorIcon() {
const icon = document.createElement('ph-warning-circle')
icon.className = 'ciam-form-text-icon'
icon.ariaHidden = 'true'
return icon
}
export default function inputValidator(
inputEl: HTMLInputElement | HTMLTextAreaElement
) {
const isDsBranded = inputEl.classList.contains('form-control-ds')
const messageEl = document.createElement('div')
messageEl.className =
inputEl.getAttribute('data-ol-validation-message-classes') ||
'small text-danger mt-2 form-text'
classNames(
'small text-danger mt-2 form-text',
{ 'form-text-ds': isDsBranded }
)
messageEl.hidden = true
const messageInnerEl = messageEl.appendChild(document.createElement('span'))
messageInnerEl.className = 'form-text-inner'
messageInnerEl.className = classNames('form-text-inner', {
'form-text-inner-ds': isDsBranded,
})
const messageTextNode = document.createTextNode('')
const iconEl = materialIcon('error')
const iconEl = isDsBranded ? dsErrorIcon() : materialIcon('error')
messageInnerEl.append(iconEl)
messageInnerEl.append(messageTextNode)
inputEl.insertAdjacentElement('afterend', messageEl)
const inputContainerEl =
inputEl.closest('.form-complex-input-container') || inputEl
inputContainerEl.insertAdjacentElement('afterend', messageEl)
// Hide messages until the user leaves the input field or submits the form.
let canDisplayErrorMessages = false
+1
View File
@@ -2,6 +2,7 @@ import './utils/webpack-public-path'
import './infrastructure/error-reporter'
import './infrastructure/hotjar'
import './features/form-helpers/hydrate-form'
import './features/form-helpers/form-phosphor-icons'
import './features/form-helpers/password-visibility'
import './features/link-helpers/slow-link'
import './features/event-tracking'
@@ -1,16 +1,14 @@
import React, { FC, ReactNode } from 'react'
import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg'
type Props = { children: ReactNode }
const CiamLayout: FC<Props> = ({ children }: Props) => (
<div className="ciam-layout ciam-enabled">
<a
href="/"
aria-label="Overleaf"
className="brand"
style={{ backgroundImage: `url("${overleafLogo}")` }}
/>
<header className="ciam-logo">
<a href="/" className="brand overleaf-ds-logo">
<span className="visually-hidden">Overleaf</span>
</a>
</header>
<div className="ciam-container">
<main className="ciam-card" id="main-content">
{children}
@@ -1,33 +1,59 @@
@use 'sass:math';
.overleaf-ds-logo {
background-image: url('../../../frontend/js/shared/svgs/overleaf-a-ds-solution-mallard.svg');
}
.ciam-layout {
@include full-height-stacked-page;
display: flex;
flex-direction: column;
gap: var(--ds-spacing-400);
@include media-breakpoint-up(sm) {
gap: var(--ds-spacing-800);
}
}
.ciam-enabled {
@include ds-body-md-regular;
font-family: var(--ds-font-family-sans), sans-serif;
--password-visibility-toggle-width: calc(24px + 2 * var(--ds-spacing-250));
&,
h1 {
font-family: var(--ds-font-family-sans), sans-serif;
color: var(--ds-color-text-primary);
}
.ciam-container {
flex: 1 1 auto;
padding: var(--ds-spacing-350);
padding: 0 var(--ds-spacing-300);
}
.brand {
.ciam-logo {
padding: var(--ds-spacing-800) 0 0 0;
text-align: center;
@include media-breakpoint-up(sm) {
padding-left: var(--ds-spacing-800);
padding-right: var(--ds-spacing-800);
}
}
.ciam-logo .brand {
flex-shrink: 0;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
height: 64px;
width: 130px;
margin: var(--ds-spacing-350) auto;
height: 49px;
width: 107px;
margin: 0 auto;
display: block;
@include media-breakpoint-up(sm) {
margin: var(--ds-spacing-350) var(--ds-spacing-800);
height: 64px;
width: 130px;
margin: 9px 0; // Vertical margin isn't an exacting spacing value in the design
}
}
@@ -49,11 +75,32 @@
padding: var(--ds-spacing-800) var(--ds-spacing-400);
border-radius: var(--ds-border-radius-400);
max-width: 464px;
margin: var(--ds-spacing-400) auto;
margin: 0 auto;
@include media-breakpoint-up(sm) {
padding: var(--ds-spacing-1300);
}
.notification {
@include ds-body-sm-regular;
color: var(--ds-color-text-primary);
padding: 0 var(--ds-spacing-400);
border-width: 0;
border-radius: var(--ds-border-radius-200);
.notification-icon {
font-size: math.div(20em, 14);
}
.notification-content {
padding: var(--ds-spacing-400) 0;
}
&.notification-type-error {
background-color: var(--ds-color-red-50);
}
}
}
.ciam-disclaimers p {
@@ -77,6 +124,7 @@
p {
@include ds-body-sm-regular;
color: var(--ds-color-text-secondary);
margin-bottom: 0;
}
}
@@ -86,25 +134,31 @@
padding: var(--ds-spacing-200) 0;
}
.ciam-stepper {
margin: 0;
height: 4px;
border-radius: var(--ds-border-radius-full);
.step {
background: var(--ds-color-neutral-200);
}
}
footer {
.footer-links {
display: flex;
gap: var(--ds-spacing-600);
justify-content: center;
padding: var(--ds-spacing-350) 0;
margin: 0 auto var(--spacing-15) auto;
display: flex;
gap: var(--ds-spacing-600);
justify-content: center;
padding: var(--ds-spacing-300) 0;
margin: 0 auto;
@include media-breakpoint-up(sm) {
margin-left: var(--ds-spacing-800);
margin-right: var(--ds-spacing-800);
justify-content: start;
}
@include media-breakpoint-up(sm) {
margin-left: var(--ds-spacing-800);
margin-right: var(--ds-spacing-800);
justify-content: start;
}
a {
text-decoration: none;
@include ds-body-sm-regular;
}
a {
@include ds-body-sm-regular;
}
}
}
@@ -1,13 +1,5 @@
@import 'ds-design-system';
.ciam-register-container {
@include full-viewport-height;
display: flex;
min-height: 100%;
flex-direction: column;
}
.ciam-register-columns {
display: flex;
flex-grow: 1;
@@ -15,10 +7,51 @@
.ciam-register-login-link {
text-align: center;
margin: var(--ds-spacing-200) 0;
margin: 0;
padding: var(--ds-spacing-200) 0;
}
.ciam-work-uni-sso {
color: var(--ds-color-text-secondary);
padding-top: var(--ds-spacing-200);
margin-bottom: var(--ds-spacing-400);
font-weight: var(--ds-font-weight-semibold);
}
.ciam-register-container {
display: flex;
flex-direction: column;
.login-register-or-text-container {
@include ds-body-xs-semibold;
gap: var(--ds-spacing-250);
padding: var(--ds-spacing-200) 0 0 0;
margin-bottom: var(--ds-spacing-400);
&::before,
&::after {
background-color: var(--ds-color-neutral-200);
}
}
.login-register-error-container {
padding-bottom: 0;
.notification {
margin-bottom: var(--ds-spacing-400);
}
}
}
.ciam-password-group {
margin-bottom: var(--ds-spacing-400);
}
.ciam-password-requirements-message {
@include ds-body-sm-regular;
color: var(--ds-color-text-secondary);
padding-top: var(--ds-spacing-200);
margin: 0;
}
@@ -1,16 +1,17 @@
// TODO: Replace `fuchsia` by the correct colors.
.ciam-enabled {
.ciam-enabled,
.website-redesign:not(.application-page) .ciam-enabled .notification {
// Links
// used in services/web/frontend/stylesheets/base/links.scss
--link-color: var(--ds-color-text-secondary);
--link-hover-color: fuchsia;
--link-color: var(--ds-color-text-primary);
--link-hover-color: var(--ds-color-text-secondary);
--link-visited-color: var(--ds-color-text-secondary);
--link-text-decoration: underline;
--link-hover-text-decoration: none;
// TODO: validate that this is correct
--link-visited-color: var(--ds-color-text-secondary);
--link-color-dark: fuchsia;
--link-hover-color-dark: fuchsia;
--link-visited-color-dark: fuchsia;
--link-text-decoration: underline;
--link-hover-text-decoration: none;
}
@@ -4,14 +4,26 @@
.form-control-ds,
.form-group-ds,
.form-text-ds,
.form-label-ds {
.form-label-ds,
.form-group-ds label,
.website-redesign .form-group-ds label,
.website-redesign .form-label-ds {
@include ds-body-sm-semibold;
--bs-body-font-family: var(--ds-font-family-sans), sans-serif;
--bs-success-rgb: 25, 117, 76; // #19754c
--bs-danger-rgb: 195, 9, 43; // #c3092b
--content-placeholder: var(--ds-color-text-disabled);
// Without this, it inherits the body's --bs-body-font-family which isn't the DS font
font-family: var(--ds-font-family-sans), sans-serif;
font-family:
var(--ds-font-family-sans), sans-serif; // Without this, it inherits the body's --bs-body-font-family which isn't the DS font
color: var(--ds-color-text-primary);
margin-bottom: var(--ds-spacing-100);
}
.form-group-ds {
margin-bottom: var(--ds-spacing-400);
}
input.form-control.form-control-ds {
@@ -83,3 +95,11 @@ input.form-control.form-control-ds {
font-size: math.div(20em, 14);
}
}
.form-complex-input-container {
position: relative;
}
.ciam-form-input-icon {
font-size: var(--ds-font-size-600);
}
@@ -1,25 +1,23 @@
.login-register-or-text-container {
padding: var(--spacing-08) 0 var(--spacing-05) 0;
margin: 0;
line-height: 1;
position: relative;
font-size: var(--font-size-02);
text-align: center;
:root {
--password-visibility-toggle-width: 35px;
}
&::before {
.login-register-or-text-container {
display: flex;
gap: var(--spacing-05);
padding: var(--spacing-08) 0 var(--spacing-05) 0;
align-items: center;
font-size: var(--font-size-02);
line-height: 1;
margin: 0;
&::before,
&::after {
content: '';
position: absolute;
display: block;
flex-grow: 1;
height: 1px;
background-color: var(--neutral-20);
left: 0;
right: 0;
top: calc(var(--spacing-08) + var(--spacing-08) / 4);
}
.login-register-or-text {
position: relative;
background-color: #fff;
padding: 0 var(--spacing-05);
}
}
@@ -39,7 +37,7 @@
.form-group-password-input {
input.form-control {
padding-right: 35px;
padding-right: var(--password-visibility-toggle-width);
}
}
@@ -47,8 +45,8 @@
position: absolute;
right: 0;
top: 0;
width: 35px; // TODO: Should this be calculated ?
height: 35px; // TODO: Should this be calculated ?
width: var(--password-visibility-toggle-width);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
+3
View File
@@ -1415,6 +1415,7 @@
"more_project_collaborators": "<0>More</0> project <0>collaborators</0>",
"more_than_one_kind_of_snippet_was_requested": "The link to open this content on Overleaf included some invalid parameters. If this keeps happening for links on a particular site, please report this to them.",
"most_popular_uppercase": "Most popular",
"must_be_at_least_n_characters": "Must be at least __n__ characters. Avoid common passwords.",
"must_be_email_address": "Must be an email address.",
"must_be_purchased_online": "Must be purchased online",
"my_library": "My Library",
@@ -2533,8 +2534,10 @@
"try_to_compile_despite_errors": "Try to compile despite errors",
"turn_off": "Turn off",
"turn_off_link_sharing": "Turn off link sharing",
"turn_off_password_visibility": "Turn off password visibility",
"turn_on": "Turn on",
"turn_on_link_sharing": "Turn on link sharing",
"turn_on_password_visibility": "Turn on password visibility",
"tutorials": "Tutorials",
"uk": "Ukrainian",
"unable_to_extract_the_supplied_zip_file": "Opening this content on Overleaf failed because the zip file could not be extracted. Please ensure that it is a valid zip file. If this keeps happening for links on a particular site, please report this to them.",
+1
View File
@@ -104,6 +104,7 @@
"@overleaf/stream-utils": "*",
"@overleaf/validation-tools": "*",
"@phosphor-icons/react": "^2.1.7",
"@phosphor-icons/webcomponents": "^2.1.5",
"@slack/webhook": "^7.0.2",
"@stripe/react-stripe-js": "^3.9.0",
"@stripe/stripe-js": "^7.7.0",