TrustRow: flex-wrap instead of a hard lg: breakpoint; sticky modal close button
TrustRow now uses flex-wrap + justify-center so badges flow as many per row as actually fit at the current width, wrapping (and auto-centering, a native flexbox behavior for the last wrapped line) instead of needing a breakpoint or risking overflow. Replaces the earlier lg:-only structural exception entirely. NewsletterModal's close button was absolutely positioned inside the dialog's own scrolling container, so it scrolled away with the content. Switched to sticky (top-6, ml-auto for horizzontal position, -mb-6 to cancel its own height so it doesn't push content down) so it stays pinned to the top-right corner while scrolling.
This commit is contained in:
@@ -156,12 +156,23 @@ export function NewsletterModal({ open, onClose }: { open: boolean; onClose: ()
|
||||
// takes over once the 2-column split itself starts at lg:.
|
||||
className="relative bg-bg-base rounded-md overflow-hidden w-full max-w-[36rem] lg:max-w-[75rem] max-h-[90vh] overflow-y-auto"
|
||||
>
|
||||
{/* sticky, not absolute — the dialog itself is the scrolling
|
||||
container (overflow-y-auto above), so an absolute-positioned
|
||||
child scrolls away with the rest of the content instead of
|
||||
staying pinned to the visible top-right corner (reported
|
||||
2026-07-29). sticky top-6 keeps it fixed to the scrolled
|
||||
viewport's top edge; ml-auto pushes it to the right within
|
||||
the dialog's normal block flow (sticky positioning doesn't
|
||||
use right-* the way absolute does); -mb-6 cancels its own
|
||||
height (size-6 = 1.5rem) so it doesn't push the modal-top
|
||||
content below it down — same visual overlap as the old
|
||||
absolute positioning, just still visible after scrolling. */}
|
||||
<button
|
||||
ref={closeButtonRef}
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
aria-label="Schließen"
|
||||
className="absolute top-6 right-6 z-10 size-6 flex items-center justify-center active:scale-90 transition-transform"
|
||||
className="sticky top-6 ml-auto mr-6 -mb-6 z-20 size-6 flex items-center justify-center active:scale-90 transition-transform"
|
||||
>
|
||||
<Image alt="" src="/icon-close.png" width={24} height={24} className="size-full object-contain" />
|
||||
</button>
|
||||
|
||||
+24
-40
@@ -11,48 +11,32 @@ export async function TrustRow() {
|
||||
if (items.length === 0) return null;
|
||||
|
||||
return (
|
||||
// Structural breakpoint is lg:, not sm: — a deliberate exception to
|
||||
// the site's sm: (640px) structural consolidation, same category as
|
||||
// Footer's legal-links row (see that component's own comment):
|
||||
// multiple whitespace-nowrap title+description badges side by side
|
||||
// don't fit in one row below ~1024px regardless of fluid scaling.
|
||||
// Confirmed via a real horizontal-overflow measurement against the
|
||||
// live site (2026-07-29): scrollWidth exceeded the viewport by
|
||||
// ~100px at 666px and ~54px at 768px before this fix.
|
||||
//
|
||||
// Outer wrapper only positions the *whole badge group* (center below
|
||||
// sm:, left-aligned sm:-lg:, centered row again at lg:) — it does NOT
|
||||
// align each badge individually. A first attempt put items-center
|
||||
// directly on this container with each badge as a sibling; since
|
||||
// flex's align-items centers each item within the container's own
|
||||
// width independently, badges with different title/description
|
||||
// lengths ended up with different left edges (not lining up with
|
||||
// each other — reported 2026-07-29, exactly the "sonst sieht es
|
||||
// schief aus" risk). Fixed by wrapping all badges in one inner
|
||||
// group (below) that's always internally left-aligned to itself,
|
||||
// then centering/left-aligning that single group as a unit here.
|
||||
<div className="w-full bg-bg-base flex justify-center sm:justify-start lg:justify-center py-8 px-[var(--layout-padding-x)]">
|
||||
{/* Inner group — shrink-to-fit width (not w-full), so it's exactly
|
||||
as wide as its widest badge; items-start keeps every badge's
|
||||
left edge flush with every other badge's, regardless of each
|
||||
one's own content width. This single group is what the outer
|
||||
wrapper above centers/left-aligns as one block. */}
|
||||
<div className="flex flex-col lg:flex-row gap-6 lg:gap-12 items-start lg:items-center">
|
||||
{items.map((item, i) => (
|
||||
<div key={item.id} className="flex items-center gap-6 lg:gap-12">
|
||||
{i > 0 && <div className="hidden lg:block h-10 w-px bg-border" />}
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="relative size-8 shrink-0">
|
||||
<Image alt="" src={item.icon} fill sizes="32px" className="object-contain" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 items-start">
|
||||
<p className="font-semibold text-body text-text-primary whitespace-nowrap">{item.title}</p>
|
||||
<p className="text-body-sm text-text-muted whitespace-nowrap">{item.description}</p>
|
||||
</div>
|
||||
// flex-wrap + justify-center instead of a hard sm:/lg: structural
|
||||
// breakpoint — CSS flexbox centers each *wrapped line* independently,
|
||||
// so badges naturally flow as many-per-row as actually fit at the
|
||||
// current width (1 per row on a narrow phone, 2 with the 3rd
|
||||
// centered below it once there's room, all 3 in one row at Desktop
|
||||
// widths) with no breakpoint math and no overflow risk, since an
|
||||
// item that doesn't fit just wraps instead of forcing a scrollbar
|
||||
// (this used to be a hard lg: exception for exactly that overflow
|
||||
// reason — see git history — no longer needed with wrap). Divider
|
||||
// bars between badges stay lg:-only for simplicity, since which
|
||||
// badges share a line isn't fixed/known ahead of time below lg:.
|
||||
<div className="w-full bg-bg-base flex flex-wrap items-center justify-center gap-x-12 gap-y-6 lg:gap-12 py-8 px-[var(--layout-padding-x)]">
|
||||
{items.map((item, i) => (
|
||||
<div key={item.id} className="flex items-center gap-6 lg:gap-12">
|
||||
{i > 0 && <div className="hidden lg:block h-10 w-px bg-border" />}
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="relative size-8 shrink-0">
|
||||
<Image alt="" src={item.icon} fill sizes="32px" className="object-contain" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5 items-start">
|
||||
<p className="font-semibold text-body text-text-primary whitespace-nowrap">{item.title}</p>
|
||||
<p className="text-body-sm text-text-muted whitespace-nowrap">{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user