Commerce & auth
Login
Refined authentication card with validation & social SSO
detent-login
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 3
- Wave
- phase-3-data
- Group
- Commerce & auth
6.9 KB gzip for the class module
Variants
Every card validates like the upstream component: submit with an empty field to see the inline
role="alert" messages, and use the eye button to reveal the password. The first card here shows the
server-error banner, the second is in its loading state (the submit label stays legible and the button is disabled).
SSO, forgot-password and sign-up each emit one composed event; the demo never authenticates, it only reports what
the user entered.
Recorded states
focus-visiblerecorded state; no setterloadingrecorded state; no settererrorrecorded state; no setter
Usage
Import
import '/components/login/define.js';
// or register it yourself
import { DetentLogin } from '/components/login/login.js';
Attributes
descriptionerroris-loadingshow-social-loginssign-up-texttitle
Properties
onForgotPasswordonSignUpClickonSocialLoginonSubmit
Slots
logo
Events
detent-submit— {}detent-forgot-password— {}detent-sign-up-click— {}detent-social-login— {}
Example
Runnable example: site/examples/login.ts. The snippet below is generated from the catalogued attributes.
<detent-login
description
error
is-loading
show-social-logins
sign-up-text
title="Welcome back"
></detent-login>
Property-only values
const element = document.querySelector('detent-login');
if (element !== null) {
// onSubmit: (data: LoginFormData) => void
element.onSubmit = () => {};
// onForgotPassword: () => void
element.onForgotPassword = () => {};
// onSignUpClick: () => void
element.onSignUpClick = () => {};
// onSocialLogin: (provider: 'github' | 'google' | 'apple') => void
element.onSocialLogin = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/Login.tsxsha256 3dc8d9a7d75d…
Native files
The native module and its artifacts:
src/components/login/login.tssrc/components/login/styles.csssrc/components/login/define.tsdocs/components/login.mdsite/examples/login.tstests/browser/login.spec.tsevidence/components/login.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
description | description | string | Sign in to access your Detent workspace | Kebab-case attribute; camelCase property. |
error | error | string | null | null | Kebab-case attribute; camelCase property. |
is-loading | isLoading | boolean | false | Presence semantics: present means true, absent means false. |
show-social-logins | showSocialLogins | boolean | true | Presence semantics: present means true, absent means false. |
sign-up-text | — | — | — | Recorded in the catalog as an attribute. |
title | title | string | 'Welcome back' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
onForgotPassword | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onSignUpClick | () => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onSocialLogin | (provider: 'github' | 'google' | 'apple') => void | undefined | Property-only: assign from script; not accepted as an attribute. |
onSubmit | (data: LoginFormData) => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-submit | onSubmit | (data: LoginFormData) => void | Composed, bubbles; fires only for a user action. |
detent-forgot-password | onForgotPassword | () => void | Composed, bubbles; fires only for a user action. |
detent-sign-up-click | onSignUpClick | () => void | Composed, bubbles; fires only for a user action. |
detent-social-login | onSocialLogin | (provider: 'github' | 'google' | 'apple') => void | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
logo | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Forgot-password and sign-up affordances are always rendered. Upstream renders them only when the corresponding callback prop exists. A custom element always has the event, so hiding the affordance would remove discoverable UI; the secondary row hides only when sign-up-text="".
- className is not an attribute. Consumer styling uses the documented parts and custom properties (PC-9.4).
- React ReactNode logo becomes the logo slot; the Lucide Sparkles default is a local SVG (PC-11.5).
- Framer Motion springs become CSS transitions, and AnimatePresence's mount/unmount of the error banner becomes hidden toggling so the live region stays in the tree for reliable announcements.
- Callbacks become composed events plus optional property hooks; property writes emit no event (PC-6.5).
- The field boundary uses a stronger grey than upstream's #1F1F1F so the input boundary reaches 3:1 (WCAG 1.4.11). Dividers keep the upstream hairline.
- Validation is client-side only and the card never authenticates; it emits detent-submit and leaves the decision to the consumer (demo widget, no network).
- Default demo copy and labels use Detent branding instead of EasyUI (pre-release rebrand).
What it does
A refined authentication card: a slotted logo, a title and description, an optional server-error banner, GitHub and Google SSO, a validated email/password form with a password visibility toggle, a remember-me checkbox and the secondary "sign up" link.
Upstream behaviour
- Built with EasyUI Form & Button architecture
- Interactive password visibility toggle with Lucide icons
- Form validation for required fields & email regex format
- Spring-animated error banners and inline field alerts
- Configurable social SSO buttons (GitHub & Google)
- Remember me checkbox and "Forgot password?" callback hooks
- Responsive mobile/desktop dimensions with atmospheric glow header
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- Forgot-password and sign-up affordances are always rendered. Upstream renders them only when the corresponding callback prop exists. A custom element always has the event, so hiding the affordance would remove discoverable UI; the secondary row hides only when sign-up-text="".
- className is not an attribute. Consumer styling uses the documented parts and custom properties (PC-9.4).
- React ReactNode logo becomes the logo slot; the Lucide Sparkles default is a local SVG (PC-11.5).
- Framer Motion springs become CSS transitions, and AnimatePresence's mount/unmount of the error banner becomes hidden toggling so the live region stays in the tree for reliable announcements.
- Callbacks become composed events plus optional property hooks; property writes emit no event (PC-6.5).
- The field boundary uses a stronger grey than upstream's #1F1F1F so the input boundary reaches 3:1 (WCAG 1.4.11). Dividers keep the upstream hairline.
- Validation is client-side only and the card never authenticates; it emits detent-submit and leaves the decision to the consumer (demo widget, no network).
- Default demo copy and labels use Detent branding instead of EasyUI (pre-release rebrand).