diff --git a/components/brave_account/resources/BUILD.gn b/components/brave_account/resources/BUILD.gn index 2994cc1c85d..d249bebdfc0 100644 --- a/components/brave_account/resources/BUILD.gn +++ b/components/brave_account/resources/BUILD.gn @@ -32,6 +32,8 @@ build_webui("build") { "brave_account_forgot_password_dialog.ts", "brave_account_password_icons.html.ts", "brave_account_password_icons.ts", + "brave_account_password_strength_meter.html.ts", + "brave_account_password_strength_meter.ts", "brave_account_sign_in_dialog.html.ts", "brave_account_sign_in_dialog.ts", ] @@ -44,6 +46,7 @@ build_webui("build") { "brave_account_entry_dialog.css", "brave_account_forgot_password_dialog.css", "brave_account_password_icons.css", + "brave_account_password_strength_meter.css", "brave_account_sign_in_dialog.css", ] diff --git a/components/brave_account/resources/brave_account_create_dialog.html.ts b/components/brave_account/resources/brave_account_create_dialog.html.ts index 2df634db6fa..4ecfdd4df4c 100644 --- a/components/brave_account/resources/brave_account_create_dialog.html.ts +++ b/components/brave_account/resources/brave_account_create_dialog.html.ts @@ -7,9 +7,11 @@ import { html } from '//resources/lit/v3_0/lit.rollup.js' import './brave_account_email_input.js' import './brave_account_password_icons.js' +import './brave_account_password_strength_meter.js' import { BraveAccountCreateDialogElement } from './brave_account_create_dialog.js' import type { EmailInputEventDetail } from './brave_account_email_input.js' import { onToggleVisibility } from './brave_account_common.js' +import type { PasswordStrengthChangedEventDetail } from './brave_account_password_strength_meter.js' export function getHtml(this: BraveAccountCreateDialogElement) { return html` @@ -47,11 +49,18 @@ export function getHtml(this: BraveAccountCreateDialogElement) { @@ -101,7 +110,7 @@ export function getHtml(this: BraveAccountCreateDialogElement) { diff --git a/components/brave_account/resources/brave_account_create_dialog.ts b/components/brave_account/resources/brave_account_create_dialog.ts index 3e0e831d442..ce5658908c2 100644 --- a/components/brave_account/resources/brave_account_create_dialog.ts +++ b/components/brave_account/resources/brave_account_create_dialog.ts @@ -3,14 +3,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { CrLitElement, css, html } from '//resources/lit/v3_0/lit.rollup.js' -import { loadTimeData } from '//resources/js/load_time_data.js' +import { CrLitElement } from '//resources/lit/v3_0/lit.rollup.js' import { BraveAccountBrowserProxy, BraveAccountBrowserProxyImpl, } from './brave_account_browser_proxy.js' -import { BraveAccountStrings } from './brave_components_webui_strings.js' import { getCss } from './brave_account_create_dialog.css.js' import { getHtml } from './brave_account_create_dialog.html.js' import { Error, makeFocusHandler } from './brave_account_common.js' @@ -22,106 +20,6 @@ import { // @ts-expect-error import { Registration } from 'chrome://resources/brave/opaque_ke.bundle.js' -class PasswordStrengthMeter extends CrLitElement { - static get is() { - return 'password-strength-meter' - } - - static override get styles() { - return css` - :host { - align-items: center; - display: flex; - gap: var(--leo-spacing-m); - justify-content: space-between; - width: 100%; - } - - :host([category='Weak']) { - --primary-color: var(--leo-color-systemfeedback-error-icon); - --secondary-color: var(--leo-color-systemfeedback-error-background); - } - - :host([category='Medium']) { - --primary-color: var(--leo-color-systemfeedback-warning-icon); - --secondary-color: var(--leo-color-systemfeedback-warning-background); - } - - :host([category='Strong']) { - --primary-color: var(--leo-color-systemfeedback-success-icon); - --secondary-color: var(--leo-color-systemfeedback-success-background); - } - - .bar { - background-color: var(--secondary-color); - border-radius: var(--leo-radius-m); - flex-grow: 1; - height: 4px; - transition: 750ms; - } - - .strength { - background-color: var(--primary-color); - border-radius: var(--leo-radius-m); - height: 100%; - transition: 750ms; - width: calc(1% * var(--strength)); - } - - .text { - color: var(--primary-color); - font: var(--leo-font-small-regular); - transition: 750ms; - } - ` - } - - override render() { - return html` -
-
-
-
- ${loadTimeData.getString( - this.category === 'Weak' - ? BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_WEAK - : this.category === 'Medium' - ? BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_MEDIUM - : BraveAccountStrings.BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_STRONG, - )} -
- ` - } - - static override get properties() { - return { - category: { type: String, reflect: true }, - strength: { type: Number }, - } - } - - override updated(changedProperties: Map) { - if (changedProperties.has('strength')) { - this.category = - this.strength < 60 ? 'Weak' : this.strength < 100 ? 'Medium' : 'Strong' - } - } - - protected accessor category: 'Weak' | 'Medium' | 'Strong' = 'Weak' - protected accessor strength: number = 0 -} - -declare global { - interface HTMLElementTagNameMap { - 'password-strength-meter': PasswordStrengthMeter - } -} - -customElements.define(PasswordStrengthMeter.is, PasswordStrengthMeter) - export class BraveAccountCreateDialogElement extends CrLitElement { static get is() { return 'brave-account-create-dialog' @@ -142,20 +40,14 @@ export class BraveAccountCreateDialogElement extends CrLitElement { isEmailValid: { type: Boolean }, isPasswordInputFocused: { type: Boolean }, isPasswordConfirmationInputFocused: { type: Boolean }, + isPasswordStrongEnough: { type: Boolean }, password: { type: String }, passwordConfirmation: { type: String }, - passwordStrength: { type: Number }, } } protected onPasswordInput(detail: { value: string }) { this.password = detail.value - this.browserProxy.password_strength_meter - .getPasswordStrength(this.password) - .then( - (value: { strength: number }) => - (this.passwordStrength = value.strength), - ) } protected onPasswordConfirmationInput(detail: { value: string }) { @@ -232,9 +124,9 @@ export class BraveAccountCreateDialogElement extends CrLitElement { protected accessor isEmailValid: boolean = false protected accessor isPasswordInputFocused: boolean = false protected accessor isPasswordConfirmationInputFocused: boolean = false + protected accessor isPasswordStrongEnough: boolean = false protected accessor password: string = '' protected accessor passwordConfirmation: string = '' - protected accessor passwordStrength: number = 0 protected registration = new Registration() protected readonly passwordFocusHandler = makeFocusHandler( diff --git a/components/brave_account/resources/brave_account_password_strength_meter.css b/components/brave_account/resources/brave_account_password_strength_meter.css new file mode 100644 index 00000000000..5e1c2160e52 --- /dev/null +++ b/components/brave_account/resources/brave_account_password_strength_meter.css @@ -0,0 +1,53 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/* #css_wrapper_metadata_start + * #type=style-lit + * #css_wrapper_metadata_end */ + +:host { + align-items: center; + display: flex; + gap: var(--leo-spacing-m); + justify-content: space-between; + width: 100%; +} + +:host([category='Weak']) { + --primary-color: var(--leo-color-systemfeedback-error-icon); + --secondary-color: var(--leo-color-systemfeedback-error-background); +} + +:host([category='Medium']) { + --primary-color: var(--leo-color-systemfeedback-warning-icon); + --secondary-color: var(--leo-color-systemfeedback-warning-background); +} + +:host([category='Strong']) { + --primary-color: var(--leo-color-systemfeedback-success-icon); + --secondary-color: var(--leo-color-systemfeedback-success-background); +} + +.bar { + background-color: var(--secondary-color); + border-radius: var(--leo-radius-m); + flex-grow: 1; + height: 4px; + transition: 750ms; +} + +.strength { + background-color: var(--primary-color); + border-radius: var(--leo-radius-m); + height: 100%; + transition: 750ms; + width: calc(1% * var(--strength)); +} + +.text { + color: var(--primary-color); + font: var(--leo-font-small-regular); + transition: 750ms; +} diff --git a/components/brave_account/resources/brave_account_password_strength_meter.html.ts b/components/brave_account/resources/brave_account_password_strength_meter.html.ts new file mode 100644 index 00000000000..9686c0a1d18 --- /dev/null +++ b/components/brave_account/resources/brave_account_password_strength_meter.html.ts @@ -0,0 +1,26 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import { html } from '//resources/lit/v3_0/lit.rollup.js' + +import { BraveAccountPasswordStrengthMeter } from './brave_account_password_strength_meter.js' + +export function getHtml(this: BraveAccountPasswordStrengthMeter) { + return html` +
+
+
+
+ ${this.category === 'Weak' + ? '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_WEAK}' + : this.category === 'Medium' + ? '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_MEDIUM}' + : '$i18n{BRAVE_ACCOUNT_PASSWORD_STRENGTH_METER_STRONG}'} +
+ ` +} diff --git a/components/brave_account/resources/brave_account_password_strength_meter.ts b/components/brave_account/resources/brave_account_password_strength_meter.ts new file mode 100644 index 00000000000..5aa6a5e8996 --- /dev/null +++ b/components/brave_account/resources/brave_account_password_strength_meter.ts @@ -0,0 +1,89 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import { + CrLitElement, + PropertyValues, +} from '//resources/lit/v3_0/lit.rollup.js' + +import { + BraveAccountBrowserProxy, + BraveAccountBrowserProxyImpl, +} from './brave_account_browser_proxy.js' +import { getCss } from './brave_account_password_strength_meter.css.js' +import { getHtml } from './brave_account_password_strength_meter.html.js' + +export type PasswordStrengthChangedEventDetail = { isStrongEnough: boolean } + +export class BraveAccountPasswordStrengthMeter extends CrLitElement { + static get is() { + return 'brave-account-password-strength-meter' + } + + static override get styles() { + return getCss() + } + + override render() { + return getHtml.bind(this)() + } + + static override get properties() { + return { + category: { type: String, reflect: true }, + strength: { type: Number, state: true }, + password: { type: String }, + } + } + + override async updated(changedProperties: PropertyValues) { + super.updated(changedProperties) + + if ((changedProperties as Map).has('password')) { + const password = this.password + try { + const { strength } = + await this.browserProxy.password_strength_meter.getPasswordStrength( + password, + ) + + // Ignore stale response if password changed while we were waiting. + if (this.password !== password) { + return + } + + this.strength = strength + } catch (error) { + console.error('Failed to calculate password strength:', error) + this.strength = 0 + } + + this.category = + this.strength < 60 ? 'Weak' : this.strength < 100 ? 'Medium' : 'Strong' + + this.fire('password-strength-changed', { + isStrongEnough: this.strength === 100, + }) + } + } + + private browserProxy: BraveAccountBrowserProxy = + BraveAccountBrowserProxyImpl.getInstance() + + protected accessor category: 'Weak' | 'Medium' | 'Strong' = 'Weak' + protected accessor strength = 0 + private accessor password = '' +} + +declare global { + interface HTMLElementTagNameMap { + 'brave-account-password-strength-meter': BraveAccountPasswordStrengthMeter + } +} + +customElements.define( + BraveAccountPasswordStrengthMeter.is, + BraveAccountPasswordStrengthMeter, +) diff --git a/resources/resource_ids.spec b/resources/resource_ids.spec index 5da7d376f12..5454d590425 100644 --- a/resources/resource_ids.spec +++ b/resources/resource_ids.spec @@ -215,7 +215,7 @@ "includes": [53980], }, "<(SHARED_INTERMEDIATE_DIR)/brave/components/brave_account/resources/resources.grd": { - "META": {"sizes": {"includes": [35]}}, + "META": {"sizes": {"includes": [40]}}, "includes": [54000], }, "brave/ios/web/test/test_resources.grd": {