Purpose#
Login is the reusable that renders and submits the login form (email + password).As the main form on the /login page (via the Login component).
Inside the LoginModal reusable (tabs: login/register).
The reusable validates fields client-side, calls the login API, shows toast messages for success/errors, and then redirects/reloads depending on context.Where it's rendered#
Login page#
In Components/Login/Default.liquid:{% render 'Reusables\\Login\\Default', uniquekey: uniquekey %}
Login modal#
In Reusables/LoginModal/Default.liquid:{% render 'Reusables\\Login\\Default', uniquekey: uniquekey %}
Login is rendered with a single parameter.Model shape (storefront example)#
Not applicable (this reusable doesn’t receive a platform model object).It receives only uniquekey from the caller (generated in Liquid) to create unique input ids/names.Required fields#
Used to generate deterministic DOM ids/names:login-email-{{ uniquekey }}
login-password-{{ uniquekey }}
field names are also suffixed with the same value.
Optional fields#
Template behavior (Liquid + Alpine)#
Source: Reusables/Login/Default.liquid.Root form uses Alpine data:
x-data="loginreusabledefault"
Submission is handled by @submit.prevent, passing translations (toast messages):
@submit.prevent="checkForm('{{ uniquekey }}', ...toast messages...)"
Password visibility is toggled via toggleVisibility($event).
Data contract (JS runtime)#
The Alpine object is loginreusabledefault from Reusables/Login/Default.js.fields (array of input elements)
errors (object map: fieldName -> errorType)
JavaScript#
Source: Reusables/Login/Default.js.init()#
Collects fields within the form (skipping .skipped, disabled, and submit buttons).
Adds blur listeners to run validateField.
Listens for a global event:
When received, it clears the form and optionally clears toasts (unless detail.preserveToasts is true).validateField(field)#
Updates CSS classes valid/invalid and optional state icons.
toggleVisibility(event)#
Finds the associated password input by converting checkbox id:toggle-login-password-<uniquekey> -> login-password-<uniquekey>
Toggles input type between password and text.
Switches icon classes (ic-eye-off / ic-preview).
updateFormValidity()#
Returns true if every field is valid (no errors + required fields filled).clearForm()#
Clears values and resets classes/icons.
checkForm(uniquekey, successMessage, errorMessage, errorMessageCredentials, errorMessageLocked)#
2.
Validates fields; stops if invalid.
servicesreusabledefault.accountLogin({ Email, Password })
If response.status == 409 → shows “locked” toast.
Otherwise → resets field visuals and shows “invalid credentials” toast.
If query starts with ?redirect=http(s):// → redirects to that target.
Else if on /login → redirects to / after a short delay.
Else if $store.loginModal.goToCheckoutPage → redirects to /checkout.
Else → reloads the current page.
Global Alpine stores#
removeAll() and add(...) for feedback.
Reads and updates goToCheckoutPage to decide whether to redirect to checkout.
Services / API calls#
servicesreusabledefault.accountLogin({ Email, Password })
Dependencies#
Reusables/Login/Default.liquid
Reusables/Login/Default.js
Reusables/Login/Default.json (translations)
Components/Login/Default.liquid (render on login page)
Reusables/LoginModal/Default.liquid (render in modal)
$store.toast and $store.loginModal in Assets/js/theme.js
servicesreusabledefault.accountLogin
Notes#
The reusable expects the generated uniquekey to be unique per instance, otherwise the document.querySelector('#login-email-' + uniquekey) selectors may target the wrong input.
For security, the redirect logic only allows absolute redirects when they come from the ?redirect=http(s)://... query string.
Examples#
{% render 'Reusables\\Login\\Default', uniquekey: uniquekey %}
{% render 'Reusables\\Login\\Default', uniquekey: uniquekey %}
Modified at 2026-04-14 13:18:56