brand: real Hanzo mark + white-label env hooks
Replace placeholder H badge with canonical Hanzo mark (abstract
H-shape vector from ~/work/hanzo/logo). Renders via currentColor.
White-label is now fully driven by env (consumed via @hanzo/sign-lib
env helper):
NEXT_PUBLIC_APP_NAME full app name
NEXT_PUBLIC_APP_NAME_PRIMARY first wordmark token
NEXT_PUBLIC_APP_NAME_SUFFIX remainder
NEXT_PUBLIC_IAM_PROVIDER_NAME IAM brand for 'Sign in with X'
Existing locale strings ('Sign in to your account', 'Welcome back')
stay as Trans-wrapped translations so the i18n pipeline keeps working.
Favicons regenerated from the canonical SVG across remix, docs,
and packages/assets.
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 787 B After Width: | Height: | Size: 869 B |
|
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
@@ -0,0 +1,57 @@
|
||||
// Canonical Hanzo mark — single-source SVG, mono via currentColor so it
|
||||
// adopts whatever text color its parent sets. Same vector shipped in
|
||||
// @hanzo/logo's docs/assets/hanzo-logo.svg, repainted to use
|
||||
// currentColor instead of #000/#666 so it composes cleanly into any
|
||||
// theme.
|
||||
//
|
||||
// White-label rule: this component renders the MARK only. The wordmark
|
||||
// (brand name + suffix) is rendered separately by the consumer using
|
||||
// NEXT_PUBLIC_APP_NAME so tenants can drop in their own brand without
|
||||
// forking the component.
|
||||
|
||||
import { cn } from '@hanzo/sign-ui/lib/utils';
|
||||
import type { SVGProps } from 'react';
|
||||
|
||||
export type HanzoMarkProps = SVGProps<SVGSVGElement> & {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export const HanzoMark = ({
|
||||
size = 24,
|
||||
className,
|
||||
...props
|
||||
}: HanzoMarkProps) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 67 67"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
aria-label="Hanzo"
|
||||
className={cn('text-current', className)}
|
||||
{...props}
|
||||
>
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor" />
|
||||
<path
|
||||
d="M0 44.6369L22.21 46.8285V44.6369H0Z"
|
||||
fill="currentColor"
|
||||
opacity="0.4"
|
||||
/>
|
||||
<path
|
||||
d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="currentColor" />
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="currentColor" />
|
||||
<path
|
||||
d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z"
|
||||
fill="currentColor"
|
||||
opacity="0.4"
|
||||
/>
|
||||
<path
|
||||
d="M66.7198 67V44.6369H44.5098V67H66.7198Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -2,8 +2,10 @@ import { Trans } from '@lingui/react/macro';
|
||||
import { redirect } from 'react-router';
|
||||
|
||||
import { getOptionalSession } from '@hanzo/sign-auth/server/lib/utils/get-session';
|
||||
import { env } from '@hanzo/sign-lib/utils/env';
|
||||
import { isValidReturnTo, normalizeReturnTo } from '@hanzo/sign-lib/utils/is-valid-return-to';
|
||||
|
||||
import { HanzoMark } from '~/components/branding/hanzo-mark';
|
||||
import { SignInForm } from '~/components/forms/signin';
|
||||
import { appMetaTags } from '~/utils/meta';
|
||||
|
||||
@@ -29,18 +31,30 @@ export async function loader({ request }: Route.LoaderArgs) {
|
||||
};
|
||||
}
|
||||
|
||||
// White-label hooks. The signin page reads brand tokens from env so a
|
||||
// tenant can drop in their own name + provider without touching code:
|
||||
//
|
||||
// NEXT_PUBLIC_APP_NAME e.g. "Hanzo eSign" | "Acme Sign"
|
||||
// NEXT_PUBLIC_APP_NAME_PRIMARY first word of wordmark
|
||||
// (defaults to first word of APP_NAME)
|
||||
// NEXT_PUBLIC_APP_NAME_SUFFIX remainder of wordmark
|
||||
// (defaults to remainder of APP_NAME)
|
||||
export default function SignIn({ loaderData }: Route.ComponentProps) {
|
||||
const { returnTo } = loaderData;
|
||||
|
||||
const appName = env('NEXT_PUBLIC_APP_NAME') || 'Hanzo eSign';
|
||||
const [defaultPrimary, ...defaultSuffixParts] = appName.split(' ');
|
||||
const primary = env('NEXT_PUBLIC_APP_NAME_PRIMARY') || defaultPrimary || appName;
|
||||
const suffix = env('NEXT_PUBLIC_APP_NAME_SUFFIX') || defaultSuffixParts.join(' ');
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen w-full items-center justify-center bg-black px-6 text-white">
|
||||
<div className="w-full max-w-sm">
|
||||
<div className="mb-12 flex items-center gap-3">
|
||||
<span className="grid h-8 w-8 place-items-center rounded-md bg-white text-base font-extrabold text-black">
|
||||
H
|
||||
</span>
|
||||
<HanzoMark size={28} className="text-white" />
|
||||
<span className="text-base font-medium tracking-tight">
|
||||
Hanzo <span className="text-zinc-400">eSign</span>
|
||||
{primary}
|
||||
{suffix && <span className="text-zinc-400"> {suffix}</span>}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 787 B After Width: | Height: | Size: 869 B |
|
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 787 B After Width: | Height: | Size: 869 B |
|
Before Width: | Height: | Size: 981 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 279 KiB |