Promote to main: Feed browser luxury redesign #7
@@ -6,6 +6,7 @@ import "./globals.css";
|
|||||||
import "../styles/type.css";
|
import "../styles/type.css";
|
||||||
import "../styles/buttons.css";
|
import "../styles/buttons.css";
|
||||||
import "../styles/avatar.css";
|
import "../styles/avatar.css";
|
||||||
|
import "../styles/signin.css";
|
||||||
import "../styles/legacy.css";
|
import "../styles/legacy.css";
|
||||||
|
|
||||||
const manrope = Manrope({
|
const manrope = Manrope({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useState, type FormEvent } from "react";
|
import { useState, type FormEvent } from "react";
|
||||||
import { signIn, type Person } from "@/lib/auth-client";
|
import { signIn, type Person } from "@/lib/auth-client";
|
||||||
import { Logo } from "./Logo";
|
import { Logo } from "./Logo";
|
||||||
|
import { SparkleButton } from "./Buttons";
|
||||||
|
|
||||||
export function SignInForm({ onSignedIn }: { onSignedIn: (p: Person) => void }) {
|
export function SignInForm({ onSignedIn }: { onSignedIn: (p: Person) => void }) {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
@@ -27,39 +28,72 @@ export function SignInForm({ onSignedIn }: { onSignedIn: (p: Person) => void })
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="center">
|
<main className="signin">
|
||||||
<form className="card" onSubmit={submit} noValidate>
|
<div className="signin-glow" aria-hidden="true" />
|
||||||
<Logo />
|
<div className="signin-col">
|
||||||
<h1>Sign in to Minnect</h1>
|
<div className="signin-brand">
|
||||||
|
<Logo size={30} />
|
||||||
|
<span className="type-wordmark">MINNECT</span>
|
||||||
|
</div>
|
||||||
|
<div className="signin-head">
|
||||||
|
<span className="type-eyebrow">Members</span>
|
||||||
|
<h1 className="signin-title">
|
||||||
|
Human answers for <em>human decisions</em>
|
||||||
|
</h1>
|
||||||
|
<div className="signin-rule" aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<form className="signin-form" onSubmit={submit} noValidate>
|
||||||
{error && (
|
{error && (
|
||||||
<p role="alert" className="error">
|
<p role="alert" className="error signin-error">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<label htmlFor="email">Email</label>
|
<label className="signin-label" htmlFor="email">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<div className="signin-field">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<rect x="3" y="5" width="18" height="14" rx="3" />
|
||||||
|
<path d="m4 7 8 6 8-6" />
|
||||||
|
</svg>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
|
placeholder="you@company.com"
|
||||||
required
|
required
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="password">Password</label>
|
</div>
|
||||||
|
<label className="signin-label signin-label-next" htmlFor="password">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<div className="signin-field">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<rect x="4.5" y="10.5" width="15" height="10" rx="2.5" />
|
||||||
|
<path d="M8 10.5V8a4 4 0 0 1 8 0v2.5" />
|
||||||
|
</svg>
|
||||||
<input
|
<input
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
placeholder="Your password"
|
||||||
required
|
required
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<button type="submit" disabled={busy || !email || !password}>
|
</div>
|
||||||
{busy ? "Signing in..." : "Sign in"}
|
<SparkleButton
|
||||||
</button>
|
type="submit"
|
||||||
|
className="signin-submit"
|
||||||
|
label={busy ? "Signing in..." : "Sign in"}
|
||||||
|
disabled={busy || !email || !password}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
here; the file goes when it is empty. Do not add new rules here.
|
here; the file goes when it is empty. Do not add new rules here.
|
||||||
Literal white and black are only text on crimson and video letterboxing. */
|
Literal white and black are only text on crimson and video letterboxing. */
|
||||||
.center { min-height: 100vh; display: grid; place-items: center; padding: 16px; }
|
.center { min-height: 100vh; display: grid; place-items: center; padding: 16px; }
|
||||||
.card { width: 100%; max-width: 400px; background: var(--tile); border: 1px solid var(--line); border-radius: 8px; padding: 32px 24px; display: flex; flex-direction: column; gap: 8px; }
|
|
||||||
.card h1 { font-size: 22px; margin: 12px 0 8px; }
|
|
||||||
label { font-size: 14px; font-weight: 500; margin-top: 8px; }
|
label { font-size: 14px; font-weight: 500; margin-top: 8px; }
|
||||||
input { font: inherit; padding: 12px; border: 1px solid var(--line-2); border-radius: 8px; background: var(--tile-2); color: var(--ink); }
|
input { font: inherit; padding: 12px; border: 1px solid var(--line-2); border-radius: 8px; background: var(--tile-2); color: var(--ink); }
|
||||||
input:focus-visible, button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
|
input:focus-visible, button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/* Sign-in page (spec "Sign in", ADR 0001). Outside .app: one centered column, no breakpoints.
|
||||||
|
Tokens only, so it follows the active theme (dark by default). */
|
||||||
|
.signin {
|
||||||
|
position: relative;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 28px 24px;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.signin-glow {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: -220px;
|
||||||
|
width: 560px;
|
||||||
|
height: 420px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(ellipse, color-mix(in srgb, var(--accent), transparent 84%), transparent 65%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.signin-col {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 342px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.signin-brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding-top: 40px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.signin-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 72px 0 40px;
|
||||||
|
}
|
||||||
|
.signin-title {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-serif), Georgia, serif;
|
||||||
|
font-size: 40px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.05;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.signin-title em { font-style: italic; font-weight: 500; }
|
||||||
|
.signin-rule {
|
||||||
|
width: 48px;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ink), transparent 40%), transparent);
|
||||||
|
}
|
||||||
|
.signin-form { display: flex; flex-direction: column; gap: 12px; }
|
||||||
|
.signin-label {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
color: var(--ink-3);
|
||||||
|
}
|
||||||
|
.signin-label-next { margin-top: 6px; }
|
||||||
|
.signin-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
height: 52px;
|
||||||
|
padding: 0 16px;
|
||||||
|
border-radius: var(--r-ctl);
|
||||||
|
background: var(--tile-2);
|
||||||
|
color: var(--ink-3);
|
||||||
|
box-shadow: inset 0 0 0 1px var(--line-2);
|
||||||
|
transition: box-shadow .25s;
|
||||||
|
}
|
||||||
|
.signin-field:focus-within {
|
||||||
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink), transparent 45%), 0 0 0 4px color-mix(in srgb, var(--ink), transparent 94%);
|
||||||
|
}
|
||||||
|
.signin-field svg { width: 18px; height: 18px; flex: none; }
|
||||||
|
.signin-field input {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
outline: 0;
|
||||||
|
background: transparent;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.signin-field input::placeholder { color: var(--ink-3); }
|
||||||
|
.signin-form .signin-submit { margin-top: 18px; width: 100%; height: 54px; }
|
||||||
|
.signin-form .signin-submit:disabled { opacity: .5; cursor: not-allowed; }
|
||||||
|
.signin-error { margin: 0; }
|
||||||
Reference in New Issue
Block a user