From 82c1b1fd43d458c491149bb209e62c6f3329d035 Mon Sep 17 00:00:00 2001
From: Marco
Date: Sun, 2 Aug 2026 07:14:49 +0000
Subject: [PATCH] Match sold-out card height to in-stock siblings
NotifyMeForm was two stacked rows (email input + full-width button),
making an out-of-stock ProductCard taller than its in-stock siblings.
Redesigned as a single input with the submit control embedded inside it
(same py-3 as AddToCartInlineButton's own button, so the row height
matches exactly), and ProductCard now skips its reserved-height
low-stock line entirely for a fully-out-of-stock product, since that
line can never apply there.
Co-Authored-By: Claude Sonnet 5
---
app/components/NotifyMeForm.tsx | 70 +++++++++++++++++----------------
app/components/ProductCard.tsx | 15 +++++--
2 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/app/components/NotifyMeForm.tsx b/app/components/NotifyMeForm.tsx
index 336976b..8bcbad7 100644
--- a/app/components/NotifyMeForm.tsx
+++ b/app/components/NotifyMeForm.tsx
@@ -2,21 +2,27 @@
import { useState } from "react";
import { isValidEmail } from "../lib/email";
+import { ArrowRightIcon } from "./ArrowRightIcon";
/**
* Replaces the (disabled) Add-to-cart button's spot once a product/variant
* is out of stock — lets a visitor leave their email to be notified once
* lib/jobs/sendBackInStockEmails.ts (Payload backend) sends the "it's
- * back" mail. Always shows the email input + submit button directly (no
- * extra click to reveal them). The resulting out-of-stock card is taller
- * than its in-stock siblings — ProductGrid.tsx/MerklisteGrid.tsx/
- * RelatedProducts.tsx all use `items-start` on their grid so that doesn't
- * cascade into pushing every other card's button down to match (see
- * AddToCartInlineButton.tsx's own comment). `productId` is the numeric
- * Payload id (`product.numericId`), NOT AddToCartInlineButton/
- * AddToCartButton's own `id`/`productId` props — those are the commerce
- * slug (see lib/payload.ts's Product.id comment) — same "numericId, not
- * id" split WishlistButton already uses.
+ * back" mail. Always shows the email input + submit control directly (no
+ * extra click to reveal them).
+ *
+ * One input with the submit control embedded inside it (absolutely
+ * positioned, not a layout sibling) — not a separate stacked button below
+ * the input. A first version stacked input + full-width "Benachrichtigen"
+ * button, which made an out-of-stock ProductCard.tsx visibly taller than
+ * its in-stock siblings (two form rows vs. one button); shrinking that
+ * stacked version's own padding was tried and reverted — the actual ask
+ * was to keep every control's height untouched and make the CARD match
+ * instead. Embedding the submit icon inside the input keeps this whole
+ * control to exactly one row, sized with the same `py-3` as
+ * AddToCartInlineButton's own "In den Warenkorb" button, so a sold-out
+ * card ends up exactly as tall as an in-stock one — see that component's
+ * own button classes.
*/
export function NotifyMeForm({ productId, variantName = "" }: { productId: number; variantName?: string }) {
const [email, setEmail] = useState("");
@@ -55,31 +61,27 @@ export function NotifyMeForm({ productId, variantName = "" }: { productId: numbe
}
return (
- // Stacked, not side-by-side — narrow product cards (shop grid, related
- // products) don't leave enough width for input + button in one row
- // without either truncating the placeholder or squeezing the button.
-
);
}
-
diff --git a/app/components/ProductCard.tsx b/app/components/ProductCard.tsx
index f63abe6..48280b7 100644
--- a/app/components/ProductCard.tsx
+++ b/app/components/ProductCard.tsx
@@ -108,9 +108,18 @@ export function ProductCard({
{!kleinunternehmer && inkl. {taxRate}% MwSt.}
{belowPrice}
- {/* Always rendered, text conditional — reserved height keeps every
- card in a row equal height regardless of low-stock status. */}
- {anyLowStock ? "Nur noch wenige verfügbar" : null}
+ {/* Reserved height keeps every IN-STOCK card in a row equal height
+ regardless of low-stock status — but a fully-out-of-stock
+ product can never show this line (anyLowStock is meaningless
+ once nothing's left to sell), so it's omitted entirely rather
+ than reserving dead space here. That dead space was exactly
+ what made a sold-out card's NotifyMeForm sit lower than
+ necessary, growing the whole card taller than its in-stock
+ siblings — see NotifyMeForm.tsx's own comment on that height
+ mismatch. */}
+ {!fullyOutOfStock && (
+ {anyLowStock ? "Nur noch wenige verfügbar" : null}
+ )}
{/* flex-1 spacer — pins every card's button to the same Y
regardless of whether the title/category line wraps. */}