Polish the public auth experience
Replace the stacked sign-in card with a responsive public shell, a local monochrome compute field, restrained grain and reduced-motion-safe drift. Share the anonymous header with Learn so public navigation stays consistent, and carry the corrected spacing through registration and profile setup.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Public auth composition: generated compute field, grain and real form UI.
|
||||
*
|
||||
* The artwork is decoration only. Auth copy and controls stay in the normal
|
||||
* document flow, so a missing image changes the mood rather than the task.
|
||||
*/
|
||||
import type { ReactNode } from 'react';
|
||||
import { PublicHeader } from './PublicHeader';
|
||||
|
||||
export function AuthShell({ children, onSignIn }: { children: ReactNode; onSignIn?: () => void }) {
|
||||
return (
|
||||
<div className="auth-shell relative isolate flex min-h-dvh min-w-0 flex-col overflow-hidden bg-bg text-fg">
|
||||
<PublicHeader current="sign-in" onSignIn={onSignIn} />
|
||||
<main className="relative grid min-w-0 flex-1 lg:grid-cols-[minmax(0,1.08fr)_minmax(30rem,0.92fr)]">
|
||||
<div
|
||||
className="auth-visual pointer-events-none absolute inset-0 lg:relative lg:inset-auto"
|
||||
aria-hidden
|
||||
>
|
||||
<div className="auth-field-motion absolute -inset-[4%]">
|
||||
<img
|
||||
src="/images/pig-compute-field.webp"
|
||||
alt=""
|
||||
className="size-full object-cover object-[46%_50%]"
|
||||
decoding="async"
|
||||
fetchPriority="high"
|
||||
/>
|
||||
</div>
|
||||
<div className="auth-visual-vignette absolute inset-0" />
|
||||
</div>
|
||||
|
||||
<section className="relative z-10 flex min-w-0 items-center justify-center px-4 py-6 sm:px-8 sm:py-10 lg:border-l lg:border-border/70 lg:bg-bg/90 lg:px-12 xl:px-16">
|
||||
<div className="w-full min-w-0 max-w-lg">{children}</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* The small amount of chrome shared by every public PIG screen.
|
||||
*
|
||||
* These routes deliberately live outside the authenticated Shell, but they
|
||||
* should still feel like the same product. Keeping the wordmark, Learn link,
|
||||
* sign-in affordance and music control here prevents the auth and shared
|
||||
* Learn pages from drifting into separate mini-sites.
|
||||
*/
|
||||
import { AudioControl } from './AudioControl';
|
||||
import { PiggyLogo } from './PiggyMark';
|
||||
import { cn } from './ui';
|
||||
|
||||
type PublicDestination = 'learn' | 'sign-in';
|
||||
|
||||
export function PublicHeader({
|
||||
current,
|
||||
onSignIn,
|
||||
}: {
|
||||
current?: PublicDestination;
|
||||
onSignIn?: () => void;
|
||||
}) {
|
||||
const itemClass = (destination: PublicDestination) =>
|
||||
cn(
|
||||
'tap relative inline-flex items-center px-2 text-sm font-medium transition-colors',
|
||||
current === destination ? 'text-fg' : 'text-muted hover:text-fg',
|
||||
current === destination &&
|
||||
'after:absolute after:inset-x-2 after:bottom-1 after:h-px after:bg-current',
|
||||
);
|
||||
|
||||
return (
|
||||
<header className="public-header relative z-30 border-b border-border/70 bg-bg/80 backdrop-blur-xl">
|
||||
<div className="mx-auto flex h-16 w-full min-w-0 max-w-7xl items-center gap-2 px-4 sm:px-6 lg:px-8">
|
||||
<a href="/" className="tap flex shrink-0 items-center rounded-lg" aria-label="PIG home">
|
||||
<PiggyLogo />
|
||||
</a>
|
||||
|
||||
<nav aria-label="Public navigation" className="ml-auto flex shrink-0 items-center gap-1">
|
||||
<a
|
||||
href="/learn"
|
||||
aria-current={current === 'learn' ? 'page' : undefined}
|
||||
className={itemClass('learn')}
|
||||
>
|
||||
Learn
|
||||
</a>
|
||||
<AudioControl className="public-header-audio" />
|
||||
{onSignIn ? (
|
||||
<button type="button" onClick={onSignIn} className={itemClass('sign-in')}>
|
||||
Sign in
|
||||
</button>
|
||||
) : current === 'sign-in' ? (
|
||||
<span aria-current="page" className={itemClass('sign-in')}>
|
||||
Sign in
|
||||
</span>
|
||||
) : (
|
||||
<a href="/" className={itemClass('sign-in')}>
|
||||
Sign in
|
||||
</a>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user