LoginPage
An email/password login form with optional passkey and remember-me, posting to your login endpoint. Localizable through AuthLocale; restyle it with snippet overrides or slotClasses.
01 Usage
<script lang="ts">
import { LoginPage } from '@urbicon-ui/auth';
import { goto } from '$app/navigation';
const onSuccess = () => goto('/');
const passkeyApiPath = '/api/auth/passkey';
</script>
<LoginPage
{onSuccess}
{passkeyApiPath}
rememberMe
/>Basic
<script lang="ts">
import { LoginPage } from '@urbicon-ui/auth';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
</script>
<LoginPage onSuccess={() => goto(resolve('/'))} passkeyApiPath="/api/auth/passkey" rememberMe />
02 Accessibility
Errors announce without stealing focus
Below the heading sit two regions that are always in the DOM and empty until something
happens: an assertive <div role="alert"> for
failures and a polite <div role="status"> for everything
else. A screen reader only announces changes inside a live region that already exists, so a
region created together with its first error stays silent. A failed sign-in therefore reaches
the reader while the caret stays in the password field.
Autofill hints on every field
Email carries type="email" with autoComplete="email", password autoComplete="current-password", and the two-factor
step inputmode="numeric" with autoComplete="one-time-code". That is what lets a
password manager fill the form and iOS/Android offer the code from the SMS or
authenticator.
Keyboard
Everything is native: Tab through the fields, Enter submits the form. The passkey button is a real <button> and is disabled while either request is in flight, so a double Enter cannot fire two logins.
Labels, aria-invalid and aria-describedby come from the Input primitive rather than being wired here.
Focus is not moved between the two steps
When the password succeeds but the account has 2FA, the form is replaced by the code field and the heading changes, but nothing moves focus there. A keyboard or screen-reader user hears the new heading only if they navigate back to it. Pair the component with your own focus call if the two-step path is your primary flow.
03 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
apiPath | string | '/api/auth/login' | API endpoint for the login request. | |
class | string | — | Extra classes on the root element. | |
csrf | CsrfClientOptions | — | CSRF cookie/header names. Only needed when the server overrides the defaults via config.csrf. Mutating requests echo the token automatically. | |
fetcher | typeof globalThis.fetch | — | Custom fetch implementation for all API calls. Defaults to the global fetch. Useful for mock backends in demos/tests or custom retry/auth layers. | |
footer | Snippet | — | Content rendered below the form, above links (e.g. terms checkbox). | |
forgotPasswordUrl | string | '/auth/forgot-password' | URL for the forgot-password page link. | |
header | Snippet | — | Content rendered above the form (e.g. social login buttons, welcome text). | |
links | Snippet | — | Replaces the link area below the form. | |
mode | passwordpasskeyboth | 'both' | Login mode. 'password' shows only email/password, 'passkey' shows only
passkey button, 'both' shows both with separator. | |
onSuccess | () => void | — | Called after successful login. | |
passkeyApiPath | string | undefined | Passkey API base path. Required when mode is 'passkey' or 'both'. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ LoginPage: { … } }}>.
Resolves after the provider defaults and before this instance's own
slotClasses, so a project-wide look lives in one place instead of being
repeated at every usage site. | |
registerUrl | string | '/auth/register' | URL for the register page link. | |
rememberMe | boolean | false | Show a "Remember me" checkbox. When checked, sends rememberMe: true in the login request body. | |
slotClasses | AuthPageSlotClasses | — | Per-slot class overrides. Keys: root, card, title, form, field, submit, error, success, links. | |
t | PartialAuthLocale | — | Locale overrides, deep-merged over the bundle registered for the active
locale — English unless registerAuthLocale ran for it. Pass any subset,
from a single string to a whole tree. | |
twoFactorApiPath | string | '/api/auth/2fa/verify' | API endpoint for the 2FA verify step. When the login response signals
twoFactorRequired, the page switches to a code-entry step that POSTs here.
Pair with createTwoFactorHandlers().verify. | |
unstyled | boolean | — | Strip all default styling. |
04 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
LoginPageProps | interface | props | 0 | — | |
PartialAuthLocale | type | helper | 1 | Consumer-facing locale input: any subset of AuthLocale. Components
accept this as their t prop and deep-merge it over the bundle registered
for the active locale — English unless registerAuthLocale ran for it — so
overriding a single string never silently blanks the rest. | |
CsrfClientOptions | interface | helper | 1 | — | |
AuthPageSlotClasses | interface | helper | 1 | Per-slot CSS class overrides for auth page components (LoginPage, RegisterPage, etc.). Each key targets a specific visual area of the page. Only the slots you override are affected. | |
DeepPartial | type | helper | 0 | Recursive partial: every branch and leaf becomes optional. | |
AuthLocale | interface | helper | 0 | The complete auth locale bundle. Every key is required: the bundles this
package ships (en, de) satisfy the full shape, and consumer overrides
enter as PartialAuthLocale, deep-merged by mergeAuthLocale over the
bundle registered for the active locale — English unless registerAuthLocale
ran for it — so component markup reads keys directly, without per-key
?? '…' fallback literals.
Placeholder convention: dynamic values use **single-brace** tokens
({n}, {name}, {email}) that the consuming component substitutes itself
via String.replace('{token}', value). There is deliberately **no**
{{…}} runtime interpolator in this package. The key-based translator twin
(authT/at) was removed, so {{…}} here would render verbatim. |
05 Installation
Import
import { LoginPage } from '@urbicon-ui/auth';