// The ecomm UI kit — React faces of the app.css primitives, matching the Claude Design
// export (ui/designs/ecomm-login-and-create-storefront-designs, hf-kit). Presentation
// only: no business rules live here (§6.2 — the SPA renders what the BFF returns).
import { useId } from "react";
import type { ReactNode } from "react";
export function Screen({
children,
horizon = false,
plain = false,
}: {
children: ReactNode;
horizon?: boolean;
plain?: boolean;
}) {
const cls = plain ? "screen screen--plain" : horizon ? "screen screen--horizon" : "screen";
return
{children}
;
}
export function TopBar({ left, right }: { left: ReactNode; right?: ReactNode }) {
return (
);
}
export function Wordmark({ size = 26 }: { size?: number }) {
return (
ecomm
);
}
export function Footer() {
return (
);
}
export function AuthCard({ children }: { children: ReactNode }) {
return {children}
;
}
export function Eyebrow({ children }: { children: ReactNode }) {
return {children}
;
}
export function Banner({
tone = "attn",
title,
children,
}: {
tone?: "attn" | "info";
title?: string;
children: ReactNode;
}) {
return (
{tone === "attn" ? "◆" : "◇"}
{title &&
{title}
}
{children}
);
}
export function PrimaryButton({
children,
busy = false,
disabled = false,
type = "submit",
onClick,
}: {
children: ReactNode;
busy?: boolean;
disabled?: boolean;
type?: "submit" | "button";
onClick?: () => void;
}) {
return (
);
}
export function Field({
label,
optional = false,
error,
helper,
inputProps,
}: {
label: string;
optional?: boolean;
error?: string | null;
helper?: string;
inputProps: React.InputHTMLAttributes;
}) {
const id = useId();
return (
{error && (
{error}
)}
{helper && !error &&
{helper}
}
);
}
export function AccountChip({ email, onSignOut }: { email: string; onSignOut: () => void }) {
return (
{email[0]?.toUpperCase()}
{email}
);
}