feat(ui): Kundenkonto im Wabenstil
- Konto-Kacheln: Icon in einer Wabe, die sich beim Hover mit Honig fuellt, einlaufender Pfeil, Serifen-Titel - Bestell-Statusleiste: Waben statt Punkte, erledigte Abschnitte fliessen nacheinander voll, aktueller Schritt pulsiert sanft - Status-Badge mit pulsierendem Punkt bei laufenden Bestellungen - Leere Zustaende mit gestrichelter Wabe und Serifen-Titel - Formularfelder: einheitlicher Honig-Schein beim Fokus - Groessere Abschnittstitel im Konto, Bestellsummen in Serifen-Preisschrift - Nur CSS-Animationen (Komponenten rendern auch auf Admin-Serverseiten); reduzierte Bewegung schaltet Puls und Fuellen ab Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
3de17ca070
commit
6ac7cc5d1f
5 files changed
+141
-35
No files matched your search
@@ -179,7 +179,7 @@ export default async function TrackOrderPage(props: PageProps<"/bestellung-verfo
|
||||
/>
|
||||
|
||||
<div className="mt-5 flex items-center justify-between gap-4">
|
||||
<p className="text-forest">{formatPrice(order.totalCents, order.currency, locale)}</p>
|
||||
<p className="font-serif text-2xl text-forest tabular-nums">{formatPrice(order.totalCents, order.currency, locale)}</p>
|
||||
{order.invoice ? (
|
||||
<Link href={`/rechnung/${order.invoice.invoiceNumber}`} className="text-sm text-forest underline">
|
||||
{t.trackOrderPage.viewInvoice}
|
||||
|
||||
@@ -203,3 +203,57 @@ p {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Konto: erledigte Abschnitte der Statusleiste "fliessen" nacheinander voll. */
|
||||
@keyframes wh-grow {
|
||||
from {
|
||||
transform: scaleX(0);
|
||||
}
|
||||
to {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Aktueller Schritt / laufender Status: sanfter Puls um die Wabe. */
|
||||
@keyframes wh-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 color-mix(in oklab, var(--color-honey) 55%, transparent);
|
||||
}
|
||||
70%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 7px transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sechseck als Form fuer Icon-Flaechen (gleiche Geometrie wie Logomark). */
|
||||
.clip-hex {
|
||||
clip-path: polygon(50% 0, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.motion-grow,
|
||||
.motion-pulse {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Formularfelder: weicher Honig-Schein beim Fokus statt nur einer
|
||||
Randfarbe — auf allen Formularen einheitlich, ohne jedes einzeln
|
||||
anzufassen. Viele Felder setzen focus:outline-none; der Schein
|
||||
(box-shadow) bleibt davon unberuehrt. */
|
||||
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
|
||||
select,
|
||||
textarea {
|
||||
transition:
|
||||
border-color 200ms var(--ease-arrive),
|
||||
box-shadow 250ms var(--ease-arrive);
|
||||
}
|
||||
input:not([type="checkbox"]):not([type="radio"]):focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-honey) 22%, transparent);
|
||||
}
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: color-mix(in oklab, var(--color-ink) 45%, transparent);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ export default async function KontoBestellungenPage() {
|
||||
) : null}
|
||||
|
||||
<div className="mt-4 flex flex-wrap items-center justify-between gap-4">
|
||||
<p className="text-forest">{formatPrice(order.totalCents, order.currency, locale)}</p>
|
||||
<p className="font-serif text-2xl text-forest tabular-nums">{formatPrice(order.totalCents, order.currency, locale)}</p>
|
||||
<div className="flex items-center gap-3">
|
||||
{order.invoice ? (
|
||||
<Link
|
||||
|
||||
@@ -33,7 +33,7 @@ export default async function KontoPage() {
|
||||
<SectionHeading eyebrow={t.account.eyebrow} title={t.account.loginRegisterTitle} />
|
||||
<div className="mt-10 grid md:grid-cols-2 md:divide-x md:divide-ink/10 gap-12 md:gap-0">
|
||||
<form action={loginAction} className="space-y-4 md:pr-12">
|
||||
<h2 className="text-lg text-forest">{t.account.login}</h2>
|
||||
<h2 className="text-3xl text-forest">{t.account.login}</h2>
|
||||
<Field name="email" label={t.account.emailLabel} type="email" required autoComplete="email" />
|
||||
<PasswordField name="password" label={t.account.passwordLabel} required autoComplete="current-password" />
|
||||
{/* Das Turnstile-Widget ist ein iframe mit FESTER Breite (rund
|
||||
@@ -55,7 +55,7 @@ export default async function KontoPage() {
|
||||
</form>
|
||||
|
||||
<form action={registerAction} className="space-y-4 md:pl-12">
|
||||
<h2 className="text-lg text-forest">{t.account.register}</h2>
|
||||
<h2 className="text-3xl text-forest">{t.account.register}</h2>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Field name="firstName" label={t.account.firstNameLabel} required autoComplete="given-name" />
|
||||
<Field name="lastName" label={t.account.lastNameLabel} required autoComplete="family-name" />
|
||||
@@ -176,7 +176,7 @@ export default async function KontoPage() {
|
||||
|
||||
<div className="mt-10 border border-ink/10 p-6 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="text-lg text-forest">{t.account.newsletterHeading}</h2>
|
||||
<h2 className="text-2xl text-forest">{t.account.newsletterHeading}</h2>
|
||||
<p className="mt-1 text-sm text-ink/70">
|
||||
{isSubscribed ? t.account.newsletterSubscribed : t.account.newsletterNotSubscribed}
|
||||
</p>
|
||||
@@ -190,7 +190,7 @@ export default async function KontoPage() {
|
||||
|
||||
<div className="mt-6 border border-ink/10 p-6 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="text-lg text-forest">{t.account.logoutAllDevicesHeading}</h2>
|
||||
<h2 className="text-2xl text-forest">{t.account.logoutAllDevicesHeading}</h2>
|
||||
<p className="mt-1 text-sm text-ink/70">
|
||||
{t.account.logoutAllDevicesDescription}
|
||||
</p>
|
||||
@@ -208,10 +208,10 @@ export default async function KontoPage() {
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<h2 className="text-lg text-forest mb-4">{t.account.privacy.heading}</h2>
|
||||
<h2 className="text-2xl text-forest mb-4">{t.account.privacy.heading}</h2>
|
||||
<div className="border border-ink/10 p-6 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<h3 className="text-sm text-forest">{t.account.privacy.downloadDataHeading}</h3>
|
||||
<h3 className="text-lg text-forest">{t.account.privacy.downloadDataHeading}</h3>
|
||||
<p className="mt-1 text-sm text-ink/70">{t.account.privacy.downloadDataDescription}</p>
|
||||
</div>
|
||||
<LinkButton href="/api/konto/datenexport" variant="secondary" className="flex-shrink-0">
|
||||
@@ -220,7 +220,7 @@ export default async function KontoPage() {
|
||||
</div>
|
||||
|
||||
<div className="mt-6 border border-red-200 bg-red-50/40 p-6">
|
||||
<h3 className="text-sm text-red-700">{t.account.privacy.deleteAccountHeading}</h3>
|
||||
<h3 className="text-lg text-red-700">{t.account.privacy.deleteAccountHeading}</h3>
|
||||
<p className="mt-1 text-sm text-ink/70">{t.account.privacy.deleteAccountDescription}</p>
|
||||
<ConfirmForm
|
||||
action={deleteOwnAccountAction}
|
||||
|
||||
Reference in new issue
Block a user