diff --git a/README.md b/README.md index ac306d2..eabc1dc 100644 --- a/README.md +++ b/README.md @@ -1408,6 +1408,39 @@ session (low-stock digest, stale-unverified-accounts report, weekly revenue report, expired-discount-code cleanup) needed no monitor changes of their own; see the Payload README's "Jobs Queue" section. +## Mobile responsive pass (2026-07-24) + +A full-site pass fixing concrete Mobile-width bugs across the Home Hero, +Navbar, Newsletter, cart, `/challenge`, `/todo-cards`, blog detail, and the +legal pages — found by testing on a real phone rather than just resizing a +desktop browser to 768px. The detailed technical lessons (aspect-ratio +distortion from mixing fluid/fixed sizing, `preserveAspectRatio="none"` SVGs, +`whileInView` failing to trigger on short/narrow viewports, etc.) are written +up as dated Gotchas in the `figma-to-nextjs` skill +(`~/.claude/skills/figma-to-nextjs/SKILL.md`) rather than duplicated here — +that's now the reference for "why" on any of this. Two changes are worth +calling out specifically since they add new shared components: + +- **`app/components/StepArrow.tsx`** — a small inline-SVG connector arrow, + replacing `/icon-arrow-connector.svg` across all three "how it works" step + sections (`/challenge`, `/todo-cards`, `/newsletter`). The old asset's + color lived in a CSS custom property scoped to the SVG file itself, so it + could never actually become the brand orange used everywhere else once + loaded via `next/image` — converting it to a real component was the only + fix, and having one shared component means a future color/shape/size + tweak is one edit instead of three. +- **`app/components/SectionTOC.tsx`'s `MobileSectionTOC`** — the + Impressum/Datenschutz/AGB/Widerruf/Versand table-of-contents sidebar was + `hidden` entirely below `lg:` (a 360px sidebar genuinely doesn't fit next + to a readable content column below that), which meant Mobile/Tablet had no + on-page navigation aid at all on these often-long pages — exactly where + scanning a long legal document by scrolling is hardest. `MobileSectionTOC` + is a `
/` accordion (no extra JS state needed) sharing the + same scroll-spy "active section" logic (extracted into a `useActiveSection` + hook) as the desktop sidebar nav, rendered as its own element right after + each page's heading — not nested inside the sidebar's `hidden lg:...` + wrapper, which would hide it too regardless of its own `lg:hidden` class. + ## Tests `npm run test:unit` (Vitest, `node` environment, no jsdom/Next.js runtime diff --git a/app/agb/page.tsx b/app/agb/page.tsx index 8e18924..e840792 100644 --- a/app/agb/page.tsx +++ b/app/agb/page.tsx @@ -7,7 +7,7 @@ import { Footer } from "../components/Footer"; import { TrustRow } from "../components/TrustRow"; import { RichText, extractHeadings } from "../components/RichText"; import { LiveRichText } from "../components/LiveRichText"; -import { SectionTOC } from "../components/SectionTOC"; +import { SectionTOC, MobileSectionTOC } from "../components/SectionTOC"; import { getLegalPage } from "../lib/payload"; export const metadata: Metadata = { @@ -39,6 +39,13 @@ export default async function AgbPage() {

Stand: Juli 2026

+ {/* MobileSectionTOC — below lg: only, see SectionTOC.tsx's own + comment. Outside the sidebar's `hidden lg:flex` wrapper below + (that wrapper's `hidden` would hide this too otherwise). */} +
+ +
+
diff --git a/app/challenge/page.tsx b/app/challenge/page.tsx index 3306957..b7d938d 100644 --- a/app/challenge/page.tsx +++ b/app/challenge/page.tsx @@ -4,6 +4,7 @@ import Image from "next/image"; import { draftMode } from "next/headers"; import { Footer } from "../components/Footer"; import { Reveal, RevealGroup, RevealItem } from "../components/Reveal"; +import { StepArrow } from "../components/StepArrow"; import { TestimonialsGrid } from "../components/TestimonialsGrid"; import { LiveTestimonialsGrid } from "../components/LiveTestimonialsGrid"; import { getTestimonials } from "../lib/payload"; @@ -251,23 +252,14 @@ export default async function ChallengePage() {
, i < steps.length - 1 ? ( - // Same /icon-arrow-connector.svg asset and rotate-on-stack - // pattern as todo-cards/newsletter's HowItWorks — this - // used to be its own hand-drawn SVG arrow, inconsistent - // with those two. Always visible (rotated 90° while - // stacked below lg, this page's own structural - // breakpoint) rather than hidden below lg like before. + // Shared StepArrow component (see its own file) — same + // rotate-on-stack pattern as todo-cards/newsletter's + // HowItWorks. Always visible (rotated 90° while stacked + // below lg, this page's own structural breakpoint) rather + // than hidden below lg like before. Bigger below lg: + // (w-8 h-8, was w-6 h-6) per explicit feedback.
- {/* object-contain — same preserveAspectRatio="none" fix - as todo-cards/newsletter's HowItWorks; the square - mobile box was stretching this into a blob. */} - +
) : null, ])} diff --git a/app/components/Blog.tsx b/app/components/Blog.tsx index d861e5f..2bc14e6 100644 --- a/app/components/Blog.tsx +++ b/app/components/Blog.tsx @@ -63,11 +63,14 @@ export async function Blog() {

+ {/* flex + arrow as its own span — see Tools.tsx's comment on + this same fix (→'s glyph baseline sits low next to text). */} - → Zum Beitrag + + Zum Beitrag @@ -109,11 +112,14 @@ export async function Blog() {

+ {/* flex + arrow as its own span — see the featured post's + own Link above / Tools.tsx's comment on this same fix. */} - → Zum Beitrag + + Zum Beitrag diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx index 133ac20..1a4dfc6 100644 --- a/app/components/Hero.tsx +++ b/app/components/Hero.tsx @@ -2,6 +2,29 @@ import Link from "next/link"; import Image from "next/image"; import { PopIn, Reveal } from "./Reveal"; +// Shared between the plain (below lg:) and Reveal-wrapped (lg:+) render — +// see the two call sites' own comment on why this needs two wrappers. +function HeroImage() { + return ( + + ); +} + export function Hero() { return (
@@ -113,26 +136,23 @@ export function Hero() { plain box-shadow reads as a hard rectangular edge against the existing corner/right/bottom mask-gradient fade below, which looked worse than no shadow at all — tried and reverted. */} + {/* No Reveal (fade-in-on-scroll) below lg: — whileInView's -80px + viewport margin means the image doesn't fade in until scrolled + that much further into view; on a short mobile viewport this + image sits right at the initial fold, so it stayed at + opacity:0 (a white gap, matching the section's own bg-bg-base) + above the fold until the user scrolled (reported 2026-07-24). + Plain, always-visible image below lg: instead; Reveal's fade + kept from lg: up, where the image is beside the text with + plenty of room and this was never an issue. */} +
+ +
- + diff --git a/app/components/Reveal.tsx b/app/components/Reveal.tsx index 500e9fc..01376f7 100644 --- a/app/components/Reveal.tsx +++ b/app/components/Reveal.tsx @@ -120,8 +120,17 @@ export function PopIn({ children, className, delay = 0 }: RevealProps) { diff --git a/app/components/SectionTOC.tsx b/app/components/SectionTOC.tsx index d1ed7c5..b869a0f 100644 --- a/app/components/SectionTOC.tsx +++ b/app/components/SectionTOC.tsx @@ -10,24 +10,12 @@ export type TOCSection = { id: string; title: string }; // active state away from what was actually clicked. const CLICK_OVERRIDE_MS = 1000; -// lg:-only sidebar — same "wide fixed-width block next to content" shape -// as the cart's order-summary sidebar (see figma-to-nextjs skill Gotcha -// #5): a 360px TOC card plus a readable content column already exceeds -// the 768px Tablet floor, so md: wouldn't leave room for a real 2-column -// split at Tablet widths. -// -// Generic over `sections` — originally written just for /versand -// (VersandTOC), generalized once /datenschutz needed the identical -// scroll-spy sidebar but driven by CMS-authored headings instead of a -// hardcoded array. Any future long legal/content page reuses this too. -// -// Not sticky itself — Impressum/Datenschutz put an extra card below this -// in the same sidebar column, and if only this