Polish the public auth experience
CI / verify (push) Successful in 3m26s
CI / publish (push) Has been skipped

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:
2026-08-13 20:51:34 -07:00
parent b63ed0181f
commit 9db53cb36f
9 changed files with 574 additions and 430 deletions
+37
View File
@@ -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>
);
}
+63
View File
@@ -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>
);
}