refactor(ui): mobile-first pass on shared chrome + shadcn Field wrapper
- Field.tsx re-added composing shadcn Label (no built-in equivalent in this CLI generation). - Bumped Input/Textarea to h-12/rounded-xl/px-4 (44px+ touch targets, matches the app's existing rounded, roomy input style). - Card padding restored to the old flat p-5 sm:p-6 default so most call sites (`<Card>children</Card>`) don't need CardContent wrapping; CardHeader/Content/Footer still exported for the few structured cards. - Swapped literal pink-500/gray-100 focus/border colors for text-primary / border-border / ring-ring tokens in Footer and CalendarButtons so brand color flows from the CSS vars. - min-h-screen -> min-h-[100dvh] on full-bleed screens (home, create, not-found) so mobile browser chrome doesn't clip content. - Responsive type scale (text-4xl sm:text-5xl md:text-6xl, etc.) instead of a single jump at md:, so headings don't overflow on narrow phones. - Root layout now mounts <Toaster> and uses bg-background/ text-foreground (background token matches the previous gray-50). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,12 +4,12 @@ export const metadata = { title: 'Create a proposal, no account needed' };
|
||||
|
||||
export default function CreatePage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 py-10 px-4">
|
||||
<div className="min-h-[100dvh] bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 py-6 sm:py-10 px-4">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<div className="text-center mb-8 text-white">
|
||||
<div className="text-5xl mb-3">💌</div>
|
||||
<h1 className="font-display text-3xl font-extrabold mb-1 text-balance">Create your proposal</h1>
|
||||
<p className="text-white/80 text-balance">No account needed, you get two private links when you're done.</p>
|
||||
<div className="text-center mb-6 sm:mb-8 text-white">
|
||||
<div className="text-4xl sm:text-5xl mb-2 sm:mb-3">💌</div>
|
||||
<h1 className="font-display text-2xl sm:text-3xl font-extrabold mb-1 text-balance">Create your proposal</h1>
|
||||
<p className="text-white/80 text-balance text-sm sm:text-base">No account needed, you get two private links when you're done.</p>
|
||||
</div>
|
||||
<CreateClient />
|
||||
</div>
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@
|
||||
--font-inter: 'Inter', system-ui, sans-serif;
|
||||
--font-poppins: 'Poppins', system-ui, sans-serif;
|
||||
|
||||
--background: 0 0% 100%;
|
||||
/* Matches the app's existing gray-50 page backdrop; cards stay pure white */
|
||||
--background: 210 20% 98%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
|
||||
+3
-1
@@ -1,6 +1,7 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import { Inter, Poppins } from 'next/font/google';
|
||||
import Footer from '@/components/Footer';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import './globals.css';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
|
||||
@@ -35,9 +36,10 @@ export const viewport: Viewport = {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" className={`${inter.variable} ${poppins.variable}`}>
|
||||
<body className="font-sans bg-gray-50 text-gray-900 min-h-screen flex flex-col">
|
||||
<body className="font-sans bg-background text-foreground min-h-screen flex flex-col">
|
||||
<div className="flex-1">{children}</div>
|
||||
<Footer />
|
||||
<Toaster position="bottom-center" />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import Link from 'next/link';
|
||||
import { buttonVariants } from '@/components/ui/Button';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 flex items-center justify-center px-4">
|
||||
<div className="bg-white rounded-3xl shadow-2xl p-12 text-center max-w-md w-full">
|
||||
<div className="min-h-[100dvh] bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 flex items-center justify-center px-4 py-8">
|
||||
<div className="bg-white rounded-3xl shadow-2xl p-8 sm:p-12 text-center max-w-md w-full">
|
||||
<div className="text-6xl mb-4">🔍</div>
|
||||
<h1 className="font-display text-3xl font-extrabold text-gray-900 mb-2">Not found</h1>
|
||||
<p className="text-gray-500 mb-8">
|
||||
This proposal doesn't exist or has been removed.
|
||||
</p>
|
||||
<Link href="/" className={buttonVariants({ size: 'lg', className: 'px-8' })}>
|
||||
<Link href="/" className={buttonVariants({ size: 'lg', className: 'px-8 w-full sm:w-auto' })}>
|
||||
Go home
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
+5
-5
@@ -3,7 +3,7 @@ import LoveFusion from '@/components/LoveFusion';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<main className="relative min-h-screen overflow-hidden bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 flex flex-col items-center justify-center px-4 py-16 text-white">
|
||||
<main className="relative min-h-[100dvh] overflow-hidden bg-gradient-to-br from-orange-400 via-pink-500 to-rose-500 flex flex-col items-center justify-center px-4 py-12 sm:py-16 text-white">
|
||||
{/* Ambient glow, purely decorative */}
|
||||
<div aria-hidden className="pointer-events-none absolute inset-0 overflow-hidden">
|
||||
<div className="absolute -top-24 -left-24 w-72 h-72 rounded-full bg-white/10 blur-3xl" />
|
||||
@@ -12,18 +12,18 @@ export default function HomePage() {
|
||||
|
||||
<div className="relative text-center max-w-2xl w-full">
|
||||
<LoveFusion className="mb-6" />
|
||||
<h1 className="font-display text-5xl md:text-6xl font-extrabold mb-4 text-balance">
|
||||
<h1 className="font-display text-4xl sm:text-5xl md:text-6xl font-extrabold mb-4 text-balance">
|
||||
The adorable way to ask someone out
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-white/80 mb-10 text-balance">
|
||||
<p className="text-lg sm:text-xl md:text-2xl text-white/80 mb-8 sm:mb-10 text-balance">
|
||||
Create a personalized proposal, share the link, and watch the magic happen.
|
||||
</p>
|
||||
|
||||
{/* Primary CTA */}
|
||||
<div className="bg-white/20 backdrop-blur-sm rounded-3xl p-6 border border-white/30 shadow-xl">
|
||||
<div className="bg-white/20 backdrop-blur-sm rounded-3xl p-4 sm:p-6 border border-white/30 shadow-xl">
|
||||
<Link
|
||||
href="/create"
|
||||
className="block w-full bg-white text-pink-600 font-extrabold px-8 py-5 rounded-2xl text-xl
|
||||
className="block w-full bg-white text-pink-600 font-extrabold px-8 py-4 sm:py-5 rounded-2xl text-lg sm:text-xl
|
||||
shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all
|
||||
focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-white/60"
|
||||
>
|
||||
|
||||
@@ -25,15 +25,17 @@ export default function CalendarButtons({ title, startsAt, description }: Props)
|
||||
href={makeGCalUrl(title, startsAt, description)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-blue-50 text-blue-700
|
||||
border border-blue-200 rounded-xl text-sm font-semibold hover:bg-blue-100 transition-colors"
|
||||
className="flex-1 flex items-center justify-center gap-2 min-h-11 px-4 py-3 bg-blue-50 text-blue-700
|
||||
border border-blue-200 rounded-xl text-sm font-semibold hover:bg-blue-100 transition-colors
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
📅 Add to Google Calendar
|
||||
</a>
|
||||
<button
|
||||
onClick={downloadIcs}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-gray-50 text-gray-700
|
||||
border border-gray-200 rounded-xl text-sm font-semibold hover:bg-gray-100 transition-colors"
|
||||
className="flex-1 flex items-center justify-center gap-2 min-h-11 px-4 py-3 bg-secondary text-secondary-foreground
|
||||
border border-border rounded-xl text-sm font-semibold hover:bg-secondary/80 transition-colors
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
📥 Download .ics
|
||||
</button>
|
||||
|
||||
+10
-10
@@ -4,16 +4,16 @@ const GITHUB_URL = 'https://github.com/jeangaston/lovelope';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-gray-100 bg-white/80 backdrop-blur-sm mt-auto">
|
||||
<div className="max-w-5xl mx-auto px-4 py-5 flex flex-col gap-3 text-sm text-gray-400">
|
||||
<footer className="border-t border-border bg-white/80 backdrop-blur-sm mt-auto pb-safe">
|
||||
<div className="max-w-5xl mx-auto px-4 py-5 flex flex-col gap-3 text-sm text-muted-foreground">
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 text-center sm:text-left">
|
||||
<span className="text-base">💌</span>
|
||||
<span>
|
||||
<Link
|
||||
href="/"
|
||||
className="font-semibold text-gray-600 hover:text-pink-500 transition-colors rounded
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pink-300"
|
||||
className="font-semibold text-gray-600 hover:text-primary transition-colors rounded
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
lovelope.app
|
||||
</Link>
|
||||
@@ -24,22 +24,22 @@ export default function Footer() {
|
||||
<span className="text-xs hidden sm:inline text-gray-300">Proposals auto-delete after 30 days</span>
|
||||
<Link
|
||||
href="/create"
|
||||
className="text-pink-500 hover:underline font-medium text-xs rounded
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pink-300"
|
||||
className="text-primary hover:underline font-medium text-xs rounded py-1
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
Create a proposal →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-3 text-xs text-gray-300 border-t border-gray-100 pt-3">
|
||||
<div className="flex items-center justify-center gap-3 text-xs text-gray-300 border-t border-border pt-3">
|
||||
<span>Made with ❤️ by jeangaston</span>
|
||||
<span className="text-gray-200">·</span>
|
||||
<a
|
||||
href={GITHUB_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 hover:text-pink-500 transition-colors rounded
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pink-300"
|
||||
className="inline-flex items-center gap-1.5 hover:text-primary transition-colors rounded
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 .5C5.65.5.5 5.65.5 12c0 5.09 3.29 9.4 7.86 10.93.57.1.79-.25.79-.55v-2.14c-3.2.7-3.87-1.36-3.87-1.36-.53-1.33-1.29-1.68-1.29-1.68-1.05-.72.08-.7.08-.7 1.17.08 1.78 1.2 1.78 1.2 1.03 1.77 2.71 1.26 3.37.96.1-.75.4-1.26.73-1.55-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a10.98 10.98 0 0 1 5.79 0c2.2-1.49 3.17-1.18 3.17-1.18.64 1.59.24 2.76.12 3.05.74.81 1.18 1.83 1.18 3.09 0 4.43-2.7 5.4-5.27 5.69.42.36.78 1.07.78 2.16v3.2c0 .3.21.66.8.55C20.21 21.39 23.5 17.08 23.5 12c0-6.35-5.15-11.5-11.5-11.5Z" />
|
||||
|
||||
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-xl border bg-card text-card-foreground shadow",
|
||||
"rounded-2xl border bg-card text-card-foreground shadow-sm p-5 sm:p-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
interface FieldProps {
|
||||
label: string;
|
||||
htmlFor?: string;
|
||||
hint?: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function Field({ label, htmlFor, hint, children }: FieldProps) {
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor={htmlFor} className="text-sm font-semibold text-foreground">
|
||||
{label}
|
||||
</Label>
|
||||
{hint && <p className="text-xs text-muted-foreground">{hint}</p>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"flex h-12 w-full rounded-xl border border-input bg-transparent px-4 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 focus-visible:border-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -9,7 +9,7 @@ const Textarea = React.forwardRef<
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"flex min-h-[80px] w-full rounded-xl border border-input bg-transparent px-4 py-3 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40 focus-visible:border-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
Reference in New Issue
Block a user