Fix several mobile responsive issues across Hero, Newsletter, cart, and Challenge
- Hero CTA: back to a single line below lg: (wrapping put the icon beside two lines and looked broken), sized down instead so the full phrase fits. - Hero heading: dropped the forced <br> after "darf" at true mobile widths, kept it only for the sm-lg tablet range it was added for. - Hero social proof: avatars+text now wrap and center instead of cramming onto one row (flex-wrap + justify-center, no fixed breakpoint needed). - Newsletter card icon: stacked above the copy and centered below md: instead of squeezed into a row beside it. - TrustRow: left-aligned below md: instead of centered (shop, cart, and every other page using this component). - Cart line item image: full width below sm: instead of a fixed 150px square. - Challenge EmailCapture (top + bottom CTA, same shared component): input and button stack full-width below sm: instead of squeezing into one row. - Challenge steps: icon above text and centered at every breakpoint (previously only from lg: up), connector arrows centered to match.
This commit is contained in:
@@ -214,8 +214,20 @@ export function CartContent({
|
||||
<div key={lineKey} className="w-full">
|
||||
{i > 0 && <div className="h-px bg-border w-full mb-6" />}
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-6 items-start sm:items-center w-full">
|
||||
<div className="relative size-[9.375rem] shrink-0 rounded-sm overflow-hidden">
|
||||
<Image src={product.image} alt={product.name} fill sizes="150px" className="object-cover" />
|
||||
{/* Full-width on mobile (stacked layout) instead of the
|
||||
fixed 150px square — a small square floating above
|
||||
the text looked cramped on a narrow column that has
|
||||
the width to spare; fixed 150px square again from
|
||||
sm: once the row layout kicks in and the image sits
|
||||
beside the text instead. */}
|
||||
<div className="relative w-full aspect-square sm:size-[9.375rem] sm:shrink-0 rounded-sm overflow-hidden">
|
||||
<Image
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
fill
|
||||
sizes="(min-width: 640px) 150px, 100vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
{discount !== null && (
|
||||
<span className="absolute top-2 left-2 rounded-full bg-brand px-2 py-0.5 text-label font-bold text-text-primary">
|
||||
-{discount}%
|
||||
|
||||
@@ -22,7 +22,13 @@ export function EmailCapture({ buttonLabel = "Challenge starten" }: { buttonLabe
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-2 w-full">
|
||||
<div className="flex gap-3 w-full">
|
||||
{/* Stacked full-width below sm: — side by side, the button's own
|
||||
content width plus the input's min-w-0 squeeze left it cramped
|
||||
on a narrow phone. Default align-items: stretch in flex-col
|
||||
mode is what makes both the input and the button (shrink-0,
|
||||
fixed to its label's width) fill the row once stacked, no
|
||||
explicit w-full needed on either. */}
|
||||
<div className="flex flex-col sm:flex-row gap-3 w-full">
|
||||
<input
|
||||
ref={emailRef}
|
||||
type="email"
|
||||
|
||||
+11
-3
@@ -231,13 +231,21 @@ export default async function ChallengePage() {
|
||||
<p className="text-[1rem] text-[#666]">Jeden Tag ein Impuls. In nur wenigen Minuten.</p>
|
||||
</Reveal>
|
||||
|
||||
<RevealGroup className="flex flex-col lg:flex-row items-start lg:items-start gap-8 lg:gap-2 w-full">
|
||||
{/* items-center below lg: (was items-start) — the step blocks
|
||||
are centered columns now (see RevealItem below), so the
|
||||
connector arrows between them need to be centered too,
|
||||
not flush against the left edge. */}
|
||||
<RevealGroup className="flex flex-col lg:flex-row items-center lg:items-start gap-8 lg:gap-2 w-full">
|
||||
{steps.flatMap((step, i) => [
|
||||
<RevealItem key={step.title} className="group flex lg:flex-col items-start lg:items-center gap-4 lg:gap-5 flex-1 min-w-0">
|
||||
// Icon-above-text, centered, at every breakpoint now
|
||||
// (previously a left-aligned icon+text row below lg: —
|
||||
// fixed 2026-07-24 to match the lg: layout instead of
|
||||
// diverging from it).
|
||||
<RevealItem key={step.title} className="group flex flex-col items-center gap-4 lg:gap-5 flex-1 min-w-0">
|
||||
<div className="flex items-center justify-center w-16 h-14 shrink-0 transition-transform duration-300 group-hover:scale-110">
|
||||
{step.icon}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 lg:text-center">
|
||||
<div className="flex flex-col gap-1 text-center">
|
||||
<p className="font-semibold text-[#222221] text-[1rem]">{step.title}</p>
|
||||
<p className="text-[0.875rem] text-[#666] leading-[1.5]">{step.desc}</p>
|
||||
</div>
|
||||
|
||||
+23
-22
@@ -22,19 +22,19 @@ export function Hero() {
|
||||
viewport — this doubles as the page's entrance animation. */}
|
||||
<Reveal className="order-1 lg:order-none lg:col-span-5 flex flex-col gap-7 items-start pl-[var(--layout-padding-x)] pr-10 lg:pr-0">
|
||||
|
||||
{/* Heading — forced break after "darf" below lg: (1024px),
|
||||
natural wrap from lg: up. Below lg: the Hero is stacked
|
||||
full-width and narrower per-viewport, where natural wrap
|
||||
produced an awkward break — force it after "darf" there via
|
||||
a responsive <br/> (visible by default, turned off at lg:+).
|
||||
From lg: up the 5/12 grid's text column wraps fine on its
|
||||
own, no forced break needed. */}
|
||||
{/* Heading — forced break after "darf" only in the sm-lg tablet
|
||||
range (Hero stacked full-width, natural wrap there landed
|
||||
awkwardly). Removed at true mobile widths (below sm:) 2026-07-24
|
||||
— narrower still, natural wrap there reads fine, and the
|
||||
forced break made "darf" the whole first line, leaving very
|
||||
little text above the CTA. Natural wrap from lg: up too, once
|
||||
the 5/12 grid gives the column real width again. */}
|
||||
<p
|
||||
className="font-semibold leading-[0] shrink-0 text-[0px] text-text-primary"
|
||||
style={{ fontFamily: "var(--font-playfair)" }}
|
||||
>
|
||||
<span className="text-display">
|
||||
Produktivität darf<br className="lg:hidden" /> sich leicht anfühlen
|
||||
Produktivität darf<br className="hidden sm:inline lg:hidden" /> sich leicht anfühlen
|
||||
</span>
|
||||
{/* Brand's signature orange dot (also in the logo/footer) —
|
||||
bouncy pop-in once the heading scrolls into view, timed to
|
||||
@@ -57,17 +57,14 @@ export function Hero() {
|
||||
href="/challenge"
|
||||
className="flex gap-4 items-center justify-center overflow-clip px-6 py-3 rounded-sm shrink-0 max-w-full bg-brand hover:brightness-95 active:scale-[0.97] transition-all"
|
||||
>
|
||||
{/* whitespace-nowrap only from lg: up, where the 5-col text
|
||||
column has room for the full phrase on one line. Below
|
||||
lg: (stacked, narrower column) nowrap forced this button
|
||||
wider than the column itself, overflowing on mobile —
|
||||
fixed 2026-07-24 by letting it wrap to two lines instead
|
||||
(leading-[2.375rem] was already sized for that). max-w-full
|
||||
on the Link above is what actually makes the wrap happen —
|
||||
without it, a flex item with items-start siblings just
|
||||
grows to fit the unwrapped text instead of respecting the
|
||||
column width. */}
|
||||
<span className="font-semibold leading-[2.375rem] text-text-primary text-h3 lg:whitespace-nowrap not-italic">
|
||||
{/* Letting this wrap to two lines below lg: (tried 2026-07-24)
|
||||
put the icon beside a two-line text block, which read as
|
||||
broken rather than intentional. Smaller fixed size below
|
||||
lg: instead, so the full phrase fits on one line within
|
||||
the stacked mobile column's width — text-h3's own 19px
|
||||
floor was still too wide for that at ~236px of available
|
||||
text budget on a 375px phone. */}
|
||||
<span className="font-semibold leading-[2.375rem] text-text-primary text-[0.8125rem] lg:text-h3 whitespace-nowrap not-italic">
|
||||
Starte mit der 7-Tage-Challenge
|
||||
</span>
|
||||
<div className="relative h-[1.1875rem] w-[1.5625rem] shrink-0">
|
||||
@@ -75,8 +72,12 @@ export function Hero() {
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Social proof */}
|
||||
<div className="flex gap-3 items-start overflow-clip shrink-0 w-full">
|
||||
{/* Social proof — flex-wrap + justify-center rather than a fixed
|
||||
breakpoint: at narrow widths avatars+text don't fit on one
|
||||
row, so the text wraps to its own line and both lines end up
|
||||
centered; from lg: up (real column width) it still fits on
|
||||
one row exactly as before, so nothing changes there. */}
|
||||
<div className="flex flex-wrap gap-3 items-center justify-center overflow-clip shrink-0 w-full">
|
||||
{/* Avatars — gap 2px, not overlapping */}
|
||||
<div className="flex gap-[0.125rem] items-center shrink-0">
|
||||
{["/avatar-1.jpg", "/avatar-2.jpg", "/avatar-3.jpg"].map((src, i) => (
|
||||
@@ -89,7 +90,7 @@ export function Hero() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="flex-[1_0_0] font-normal leading-[1.5rem] text-text-primary text-body [word-break:break-word]">
|
||||
<p className="flex-[1_0_0] font-normal leading-[1.5rem] text-text-primary text-body text-center [word-break:break-word]">
|
||||
10.000+ Menschen vertrauen <span className="whitespace-nowrap">einfach-produktiv</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -46,8 +46,14 @@ export function Newsletter({
|
||||
{/* Rounded card: cream bg, stacks below md */}
|
||||
<Reveal className="bg-bg-muted flex flex-col md:flex-row gap-8 md:gap-12 items-center px-8 py-8 md:py-0 rounded-md w-full">
|
||||
|
||||
{/* Left: copy — fixed width from md+ so the form always gets the remaining space */}
|
||||
<div className="flex gap-8 items-start w-full md:w-[var(--newsletter-copy-width)] md:py-4 md:shrink-0">
|
||||
{/* Left: copy — fixed width from md+ so the form always gets the remaining space.
|
||||
Icon+text stacked (icon on top, centered) below md: — side by
|
||||
side they squeezed the text into a ~164px column on a 375px
|
||||
phone (icon width + gap eating most of the card's inner
|
||||
width), wrapping awkwardly. Row layout with the icon beside
|
||||
the text is fine again from md+, where the fixed copy-column
|
||||
width leaves real room. */}
|
||||
<div className="flex flex-col items-center gap-4 text-center w-full md:flex-row md:items-start md:gap-8 md:text-left md:w-[var(--newsletter-copy-width)] md:py-4 md:shrink-0">
|
||||
|
||||
{/* Decorative envelope icon, tilted -4° as per design */}
|
||||
<div className="flex items-center justify-center shrink-0 w-[4.23rem] h-[3.71rem]">
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function TrustRow() {
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="w-full bg-bg-base flex flex-col md:flex-row gap-6 md:gap-12 items-center justify-center py-8 px-[var(--layout-padding-x)]">
|
||||
<div className="w-full bg-bg-base flex flex-col md:flex-row gap-6 md:gap-12 items-start md:items-center justify-center py-8 px-[var(--layout-padding-x)]">
|
||||
{items.map((item, i) => (
|
||||
<div key={item.id} className="flex items-center gap-6 md:gap-12">
|
||||
{i > 0 && <div className="hidden md:block h-10 w-px bg-border" />}
|
||||
|
||||
Reference in New Issue
Block a user