From 6a6d50abdba6e5f7267e97b868174d0950978cfc Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 24 Jul 2026 19:17:56 +0000 Subject: [PATCH] Fix orange dot/hero image visibility, arrow alignment, and add a mobile legal-page TOC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PopIn (Home Hero's brand dot) switched from whileInView to animate — its translate-based entrance could push the element off-screen on a narrow phone before the IntersectionObserver ever saw it as visible, leaving it stuck invisible permanently. - Hero image: no longer wrapped in Reveal below lg: — whileInView's margin meant it stayed at opacity:0 (a white gap above the fold) on short mobile viewports until scrolled. Reveal's fade-in kept from lg: up. - "→ Label" CTA links (Tools.tsx, Blog.tsx) now use a flex row with the arrow as its own span instead of a literal inline "→" character, which doesn't reliably align to the surrounding text's cap-height. - Replaced icon-arrow-connector.svg with a new shared StepArrow component (inline SVG) across all three step sections — the old asset's color couldn't be overridden from outside the SVG file, so it could never actually become brand-orange. Bigger and better-shaped below the structural breakpoint per feedback. - Added MobileSectionTOC (SectionTOC.tsx) — a
accordion shown below lg: on Impressum/Datenschutz/AGB/Widerruf/Versand, which previously had no on-page navigation aid at all below lg: (the sidebar TOC is `hidden` entirely there). - Updated the figma-to-nextjs skill with 8 new dated Gotchas from this mobile-responsive pass, and expanded Step 6's verification checklist. - README: new "Mobile responsive pass" section summarizing the above. --- README.md | 33 +++++++++ app/agb/page.tsx | 9 ++- app/challenge/page.tsx | 24 +++---- app/components/Blog.tsx | 14 ++-- app/components/Hero.tsx | 54 ++++++++++----- app/components/Reveal.tsx | 13 +++- app/components/SectionTOC.tsx | 87 +++++++++++++++++++----- app/components/StepArrow.tsx | 24 +++++++ app/components/Tools.tsx | 11 ++- app/datenschutz/page.tsx | 9 ++- app/impressum/page.tsx | 9 ++- app/newsletter/components/HowItWorks.tsx | 26 +++---- app/todo-cards/components/HowItWorks.tsx | 22 ++---- app/versand/components/VersandTOC.tsx | 9 ++- app/versand/page.tsx | 8 ++- app/widerruf/page.tsx | 9 ++- 16 files changed, 264 insertions(+), 97 deletions(-) create mode 100644 app/components/StepArrow.tsx 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