Domphy

Login05

A Auth block/component from shadcn/ui — clean-room reimplemented for Domphy (see methodology). Call Login05() with no arguments for a working demo, or edit the code below live.

Implementation notes

Passwordless entry point: header row (logo badge + bold title, sign-up link right-aligned), single required email field, primary Login button, 'Or' divider, Apple/Google outline fallback buttons, legal footer. No password field / no forgot-password link, matching the spec. Doctor diagnose(): 0 findings.

Status: ported · Reference: shadcn/ui original

// shadcn/ui "login-05" block — clean-room reimplementation.
//
// A minimal passwordless sign-in page collecting only an email address,
// with Apple/Google fallback buttons. No password field and no
// forgot-password affordance — submitting is meant to trigger a magic-link
// handler. See ./login01-05-shared.ts for the reusable field/button/divider
// pieces.
//
// Implemented purely from the block's public functional/visual spec — no
// upstream shadcn/ui source was viewed or copied.

import type { DomphyElement } from "@domphy/core";
import { themeSpacing } from "@domphy/theme";
import { strong } from "@domphy/ui";
import {
  NARROW_CARD_WIDTH,
  brandBadge,
  dividerRow,
  emailField,
  legalFooter,
  oauthButton,
  signUpLine,
  submitButton,
} from "./login01-05-shared.js";

/** Props for {@link Login05}. */
export interface Login05Props {
  title?: string;
  signUpPrompt?: string;
  signUpLabel?: string;
  signUpHref?: string;
  emailLabel?: string;
  emailPlaceholder?: string;
  primaryButtonLabel?: string;
  dividerText?: string;
  appleButtonLabel?: string;
  onAppleClick?: () => void;
  googleButtonLabel?: string;
  onGoogleClick?: () => void;
  termsLabel?: string;
  termsHref?: string;
  privacyLabel?: string;
  privacyHref?: string;
  onSubmit?: (values: { email: string }) => void;
}

/**
 * shadcn/ui "login-05" — passwordless (magic-link) sign-in: a single email
 * field plus Apple/Google fallbacks. Call with no arguments for a fully
 * working demo.
 */
function Login05(props: Login05Props = {}): DomphyElement<"div"> {
  const {
    title = "Welcome to Acme Inc.",
    signUpPrompt = "Don't have an account?",
    signUpLabel = "Sign up",
    signUpHref = "#",
    emailLabel = "Email",
    emailPlaceholder = "m@example.com",
    primaryButtonLabel = "Login",
    dividerText = "Or",
    appleButtonLabel = "Continue with Apple",
    onAppleClick,
    googleButtonLabel = "Continue with Google",
    onGoogleClick,
    termsLabel = "Terms of Service",
    termsHref = "#",
    privacyLabel = "Privacy Policy",
    privacyHref = "#",
    onSubmit,
  } = props;

  const headerRow: DomphyElement<"div"> = {
    div: [
      {
        div: [brandBadge(), { strong: title, $: [strong()] }],
        style: { display: "flex", alignItems: "center", gap: themeSpacing(2) },
      },
      signUpLine({ promptText: signUpPrompt, linkLabel: signUpLabel, href: signUpHref, align: "start" }),
    ],
    style: {
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      flexWrap: "wrap",
      gap: themeSpacing(2),
      marginBlockEnd: themeSpacing(6),
    },
  };

  return {
    div: [
      {
        div: [
          headerRow,
          {
            form: [
              emailField({ id: "login05-email", fieldLabel: emailLabel, placeholder: emailPlaceholder }),
              submitButton(primaryButtonLabel),
              dividerRow(dividerText),
              oauthButton({ brand: "apple", visibleLabel: appleButtonLabel, accessibleLabel: appleButtonLabel, onClick: onAppleClick }),
              oauthButton({ brand: "google", visibleLabel: googleButtonLabel, accessibleLabel: googleButtonLabel, onClick: onGoogleClick }),
            ],
            onSubmit: (event) => {
              event.preventDefault();
              const data = new FormData(event.target as HTMLFormElement);
              onSubmit?.({ email: String(data.get("email") ?? "") });
            },
            style: { display: "flex", flexDirection: "column", gap: themeSpacing(4) },
          },
          {
            div: [legalFooter({ termsLabel, termsHref, privacyLabel, privacyHref })],
            style: { marginBlockStart: themeSpacing(6) },
          },
        ],
        style: { width: "100%", maxWidth: NARROW_CARD_WIDTH },
      },
    ],
    style: {
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      minHeight: "100dvh",
      padding: themeSpacing(6),
    },
  };
}

export { Login05 };

← Back to shadcn/ui catalog