feat(ui): Bild-Platzhalter im Stil des Glas-Etiketts

Fehlt ein Foto, zeigte die Seite einen Farbverlauf mit Diagonallinien -
ein generisches Muster, das auf Produkt- und Inhaltskacheln wie
unfertige Vorlage wirkte. Jetzt: ruhige Markenflaeche mit leisem
Wabenraster, das gezeichnete Waben-Zeichen und darunter der Name in
gesperrten Serif-Versalien, wie auf dem Etikett. In kleinen Groessen
(Warenkorb, Galerie-Vorschau) bleibt per Container Query nur das Zeichen.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-26 01:46:59 +02:00
1 parent 7e467a9ddf
commit 7c305a3fce
1 file changed
+30 -33
+30 -33
View File
@@ -1,7 +1,20 @@
import { HoneycombPattern } from "@/components/honeycomb";
import { Logomark } from "@/components/logomark";
const SURFACES = {
forest: "bg-forest text-cream",
cream: "bg-cream text-forest",
honey: "bg-honey-pale/60 text-forest",
} as const;
/** /**
* Abstrakter, hochwertiger Bild-Platzhalter (Farbverlauf + feine Linientextur) * Platzhalter, solange fuer ein Produkt oder einen Abschnitt kein Foto
* fuer Bereiche, in denen echte Produkt-/Naturfotografie noch fehlt. Bewusst * gepflegt ist. Gestaltet wie das Glas-Etikett: ruhige Flaeche, das
* ohne Comic-Motive oder Wabenmuster-Kitsch, passend zur Markenpalette. * gezeichnete Waben-Zeichen und darunter der Name in gesperrten
* Serif-Versalien — statt eines generischen Farbverlaufs mit Linientextur.
*
* In kleinen Groessen (Warenkorb, Galerie-Vorschau) bleibt nur das Zeichen;
* der Name erscheint erst ab 9rem Breite (Container Query).
*/ */
export function PlaceholderImage({ export function PlaceholderImage({
className = "", className = "",
@@ -9,39 +22,23 @@ export function PlaceholderImage({
label, label,
}: { }: {
className?: string; className?: string;
tone?: "forest" | "cream" | "honey"; tone?: keyof typeof SURFACES;
label?: string; label?: string;
}) { }) {
const gradients: Record<string, string> = {
forest: "from-[#1a271f] via-[#24352b] to-[#3a4d3f]",
cream: "from-[#efe8d8] via-[#f4efe5] to-[#e2d9c4]",
honey: "from-[#8a6832] via-[#b88a44] to-[#c9a468]",
};
return ( return (
<div <div className={`@container relative overflow-hidden ${SURFACES[tone]} ${className}`}>
className={`relative overflow-hidden bg-gradient-to-br ${gradients[tone]} ${className}`} <HoneycombPattern className="opacity-[0.07]" size={20} fade="radial" />
> <div className="absolute inset-0 flex flex-col items-center justify-center gap-4 p-4 text-center">
<svg <Logomark
className="absolute inset-0 w-full h-full opacity-[0.15]" className="aspect-square h-auto w-[26%] max-w-20 opacity-70"
xmlns="http://www.w3.org/2000/svg" variant={tone === "forest" ? "inverted" : "default"}
> />
<defs> {label ? (
<pattern id="lines" width="40" height="40" patternUnits="userSpaceOnUse"> <span className="hidden max-w-[80%] font-serif text-base uppercase leading-snug tracking-[0.2em] opacity-60 @min-[9rem]:line-clamp-2">
<path d="M0 40L40 0" stroke="currentColor" strokeWidth="0.5" /> {label}
</pattern> </span>
</defs> ) : null}
<rect width="100%" height="100%" fill="url(#lines)" /> </div>
</svg>
{label ? (
<span
className={`absolute bottom-4 left-4 right-4 truncate text-[11px] tracking-wide-caps ${
tone === "cream" ? "text-forest/50" : "text-cream/50"
}`}
>
{label}
</span>
) : null}
</div> </div>
); );
} }