feat(ui): dashboard ribilanciata + trial banner d'impatto + masthead minimale
Tre fix di vestizione dopo il primo "sguardo dal vivo" della dashboard loggata: 1. **Masthead minimale**: rimosse "ANNO I · N. 01" e data corrente che davano un sapore "magazine vintage" stridente con la nuova typography Geist. Ora solo logo a sx + (opzionale) etichetta sezione a dx, divider sottile. 2. **TrialBanner d'impatto**: era un avviso sussurrato (bordo dashed oro su crema). Adesso card scura full-width con mesh-blob oro animato, numero giorni in mega 4xl/5xl con accento oro, CTA bottone solido oro→pomodoro al hover. Diventa il primo focus visivo della dashboard, coerente col fatto che è la conversion principale. 3. **Dashboard bilanciata**: hero "Piano della settimana" allargata da `md:col-span-6` a `md:col-span-7` + rotazione ridotta da `-rotate-1` a `-rotate-[0.5deg]` (meno dramma). La nav resta su 5 col da col-start-8, ma con un `pl-2` chiude lo spazio fra hero e prima voce, eliminando il "buco" centrale di aria. Rimossi anche chapter marker gigante "I" e vertical text "Continua la lettura" che rinforzavano il sapore magazine. Lint/typecheck/build verdi in locale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
38f28e015b
commit
0722abc070
2 files changed
+57
-64
No files matched your search
@@ -837,23 +837,6 @@ function SignedInDashboard({
|
|||||||
className="mesh-blob mesh-blob--oro animate-float-z bottom-12 right-1/4 h-72 w-72 opacity-30"
|
className="mesh-blob mesh-blob--oro animate-float-z bottom-12 right-1/4 h-72 w-72 opacity-30"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Chapter marker gigante "I" come art di sfondo */}
|
|
||||||
<span
|
|
||||||
aria-hidden
|
|
||||||
className="font-display text-stroke-thin text-chapter pointer-events-none absolute -right-4 -top-12 select-none italic"
|
|
||||||
>
|
|
||||||
I
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{/* Eyebrow verticale a destra */}
|
|
||||||
<div
|
|
||||||
aria-hidden
|
|
||||||
className="pointer-events-none absolute right-0 top-32 hidden md:block"
|
|
||||||
style={{ writingMode: 'vertical-rl' }}
|
|
||||||
>
|
|
||||||
<p className="text-ink-soft font-mono text-xs uppercase tracking-[0.4em]">{t('lettura')}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Greeting bleed-left, sborda fuori dal container */}
|
{/* Greeting bleed-left, sborda fuori dal container */}
|
||||||
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-12 font-medium [animation-delay:120ms]">
|
<h1 className="font-display text-mega bleed-left animate-fade-up relative mt-12 font-medium [animation-delay:120ms]">
|
||||||
<span className="text-ink block">Buongiorno,</span>
|
<span className="text-ink block">Buongiorno,</span>
|
||||||
@@ -869,15 +852,10 @@ function SignedInDashboard({
|
|||||||
|
|
||||||
<TrialBanner billing={billing} />
|
<TrialBanner billing={billing} />
|
||||||
|
|
||||||
{/* Nav asimmetrica: card grande "/plan" a sinistra, le altre 4 in colonna a destra */}
|
{/* Nav asimmetrica bilanciata: card grande "/plan" 7 col a sx, lista 5 col a dx, gap chiuso */}
|
||||||
<div className="animate-fade-up relative mt-20 grid grid-cols-12 gap-6 [animation-delay:360ms]">
|
<div className="animate-fade-up relative mt-20 grid grid-cols-12 gap-6 [animation-delay:360ms]">
|
||||||
<DashboardHero
|
<DashboardHero href="/plan" eyebrow="Cucina" title={t('viewPlan')} accent="oliva" />
|
||||||
href="/plan"
|
<ul className="col-span-12 space-y-px md:col-span-5 md:col-start-8 md:pl-2">
|
||||||
eyebrow="Capitolo I · Cucina"
|
|
||||||
title={t('viewPlan')}
|
|
||||||
accent="oliva"
|
|
||||||
/>
|
|
||||||
<ul className="col-span-12 space-y-px md:col-span-5 md:col-start-8">
|
|
||||||
<NavItem href="/shopping" label={t('viewShopping')} eyebrow="Spesa" chapter="II" />
|
<NavItem href="/shopping" label={t('viewShopping')} eyebrow="Spesa" chapter="II" />
|
||||||
<NavItem href="/fasting" label={t('viewFasting')} eyebrow="Digiuno" chapter="III" />
|
<NavItem href="/fasting" label={t('viewFasting')} eyebrow="Digiuno" chapter="III" />
|
||||||
<NavItem href="/tracking" label={t('viewTracking')} eyebrow="Tracking" chapter="IV" />
|
<NavItem href="/tracking" label={t('viewTracking')} eyebrow="Tracking" chapter="IV" />
|
||||||
@@ -916,7 +894,7 @@ function DashboardHero({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={href}
|
href={href}
|
||||||
className="hover-lift border-ink/15 group col-span-12 -rotate-1 overflow-hidden border md:col-span-6"
|
className="hover-lift border-ink/15 group col-span-12 -rotate-[0.5deg] overflow-hidden border md:col-span-7"
|
||||||
style={{ background: tintGrad }}
|
style={{ background: tintGrad }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -993,42 +971,65 @@ function TrialBanner({ billing }: { billing: BillingStatus | null }) {
|
|||||||
if (derived.kind === 'trial' && derived.trialDaysRemaining != null) {
|
if (derived.kind === 'trial' && derived.trialDaysRemaining != null) {
|
||||||
const endsAt = derived.accessEndsAt ? ITALIAN_DATE.format(new Date(derived.accessEndsAt)) : '—';
|
const endsAt = derived.accessEndsAt ? ITALIAN_DATE.format(new Date(derived.accessEndsAt)) : '—';
|
||||||
return (
|
return (
|
||||||
<aside className="border-oro/35 bg-oro/5 animate-fade-up relative mt-12 border-2 border-dashed p-6 [animation-delay:300ms] md:ml-[16.66%]">
|
<aside className="bg-ink animate-fade-up relative mt-12 overflow-hidden [animation-delay:300ms]">
|
||||||
<div className="flex flex-col gap-3 md:flex-row md:items-baseline md:justify-between">
|
<div
|
||||||
<div>
|
aria-hidden
|
||||||
<p className="editorial-eyebrow">{t('trialBannerTitle')}</p>
|
className="mesh-blob mesh-blob--oro animate-float-x -right-20 -top-20 h-72 w-72 opacity-60"
|
||||||
<p className="font-display text-ink mt-2 text-lg leading-snug">
|
/>
|
||||||
{t('trialBannerBody', { days: derived.trialDaysRemaining, endsAt })}
|
<div className="relative grid items-center gap-8 p-8 md:grid-cols-12 md:gap-12 md:p-12">
|
||||||
|
<div className="md:col-span-7">
|
||||||
|
<p className="editorial-eyebrow text-carta-light/60">{t('trialBannerTitle')}</p>
|
||||||
|
<p className="font-display text-carta mt-3 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl">
|
||||||
|
<span className="text-oro tabular-nums">{derived.trialDaysRemaining}</span>{' '}
|
||||||
|
{derived.trialDaysRemaining === 1 ? 'giorno' : 'giorni'}
|
||||||
|
<span className="text-pomodoro">.</span>
|
||||||
|
</p>
|
||||||
|
<p className="font-display text-carta/70 mt-3 max-w-md text-base italic leading-snug">
|
||||||
|
Tutto sbloccato fino al {endsAt}. Poi serve un abbonamento per generare nuovi piani.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="md:col-span-5 md:text-right">
|
||||||
<Link
|
<Link
|
||||||
href="/billing"
|
href="/billing"
|
||||||
className="text-oro hover:text-pomodoro shrink-0 font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px]"
|
className="bg-oro text-ink hover:bg-pomodoro hover:text-carta inline-flex items-center gap-3 px-7 py-4 font-mono text-[11px] font-medium uppercase tracking-[0.18em] transition-colors"
|
||||||
>
|
>
|
||||||
{t('trialBannerCta')} →
|
{t('trialBannerCta')}
|
||||||
|
<span aria-hidden>→</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derived.kind === 'trial_expired' || derived.kind === 'past_due') {
|
if (derived.kind === 'trial_expired' || derived.kind === 'past_due') {
|
||||||
return (
|
return (
|
||||||
<aside className="border-pomodoro/40 bg-pomodoro/5 animate-fade-up relative mt-12 border-2 border-dashed p-6 [animation-delay:300ms] md:ml-[16.66%]">
|
<aside className="bg-ink animate-fade-up relative mt-12 overflow-hidden [animation-delay:300ms]">
|
||||||
<div className="flex flex-col gap-3 md:flex-row md:items-baseline md:justify-between">
|
<div
|
||||||
<div>
|
aria-hidden
|
||||||
<p className="editorial-eyebrow">{t('trialEndedBannerTitle')}</p>
|
className="mesh-blob mesh-blob--pomodoro animate-float-x -right-20 -top-20 h-72 w-72 opacity-65"
|
||||||
<p className="font-display text-ink mt-2 text-lg leading-snug">
|
/>
|
||||||
|
<div className="relative grid items-center gap-8 p-8 md:grid-cols-12 md:gap-12 md:p-12">
|
||||||
|
<div className="md:col-span-7">
|
||||||
|
<p className="editorial-eyebrow text-carta-light/60">{t('trialEndedBannerTitle')}</p>
|
||||||
|
<p className="font-display text-carta mt-3 text-4xl font-medium leading-[1.05] tracking-tight md:text-5xl">
|
||||||
|
{t('trialEndedBannerTitle')}
|
||||||
|
<span className="text-pomodoro">.</span>
|
||||||
|
</p>
|
||||||
|
<p className="font-display text-carta/70 mt-3 max-w-md text-base italic leading-snug">
|
||||||
{t('trialEndedBannerBody')}
|
{t('trialEndedBannerBody')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="md:col-span-5 md:text-right">
|
||||||
<Link
|
<Link
|
||||||
href="/billing"
|
href="/billing"
|
||||||
className="text-pomodoro hover:text-ink shrink-0 font-mono text-[11px] uppercase tracking-widest underline decoration-[1.5px] underline-offset-[5px]"
|
className="bg-pomodoro text-carta hover:bg-oro hover:text-ink inline-flex items-center gap-3 px-7 py-4 font-mono text-[11px] font-medium uppercase tracking-[0.18em] transition-colors"
|
||||||
>
|
>
|
||||||
{t('trialEndedBannerCta')} →
|
{t('trialEndedBannerCta')}
|
||||||
|
<span aria-hidden>→</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,40 +3,32 @@ import Link from 'next/link';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface MastheadProps {
|
interface MastheadProps {
|
||||||
|
/**
|
||||||
|
* Etichetta opzionale a destra (es. "N. 05 — Cucina"). Se omessa, l'header
|
||||||
|
* resta minimale con solo il logo a sinistra. Tono più \"app\" che \"magazine\".
|
||||||
|
*/
|
||||||
issueLabel?: string;
|
issueLabel?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FORMATTER = new Intl.DateTimeFormat('it-IT', {
|
|
||||||
weekday: 'long',
|
|
||||||
day: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
year: 'numeric',
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-of-page editorial header. Three columns on desktop:
|
* Top-of-page header minimale: logo a sinistra, sezione corrente a destra (se
|
||||||
* ┌──────────────────┬─────────────────────┬──────────────────┐
|
* fornita). Una riga sottile sotto fa da divider editoriale.
|
||||||
* │ KP · monogram │ ANNO I · N. 01 │ Lunedì 29 aprile │
|
|
||||||
* └──────────────────┴─────────────────────┴──────────────────┘
|
|
||||||
* On mobile collapses to a row with monogram + date only.
|
|
||||||
*/
|
*/
|
||||||
export function Masthead({ issueLabel = 'ANNO I · N. 01', className }: MastheadProps) {
|
export function Masthead({ issueLabel, className }: MastheadProps) {
|
||||||
const today = FORMATTER.format(new Date());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={cn('relative pb-5 pt-6', className)}>
|
<header className={cn('relative pb-5 pt-6', className)}>
|
||||||
<div className="rule-thick mb-4" />
|
<div className="flex items-center justify-between gap-4">
|
||||||
<div className="flex items-baseline justify-between gap-4">
|
<Link
|
||||||
<Link href="/" className="monogram tracking-tight" aria-label="KetoPath">
|
href="/"
|
||||||
|
className="monogram tracking-tight transition-opacity hover:opacity-70"
|
||||||
|
aria-label="KetoPath"
|
||||||
|
>
|
||||||
KetoPath<span className="text-pomodoro">.</span>
|
KetoPath<span className="text-pomodoro">.</span>
|
||||||
</Link>
|
</Link>
|
||||||
<p className="editorial-eyebrow hidden sm:block">{issueLabel}</p>
|
{issueLabel ? <p className="editorial-eyebrow hidden sm:block">{issueLabel}</p> : null}
|
||||||
<p className="text-ink-soft font-mono text-[11px] tracking-wide first-letter:uppercase">
|
|
||||||
{today}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="rule mt-4" />
|
<div className="rule mt-5" />
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user