Schnelle Bildergalerie mit Claude Code bauen
Responsive Bildergalerie mit Claude Code: React-Code, srcset, Lightbox, typische Fehler und Prüfungen.
Eine Bildergalerie ist mehr als ein Layout
Eine Bildergalerie mit Claude Code zu bauen heißt nicht nur, ein paar Bilder hübsch anzuordnen. Die Galerie kann ein Produkt verkaufen, einen Case Study belegen, Workshop-Material zeigen oder technische Screenshots vergleichbar machen. Wenn der Prompt nur “baue eine schöne Masonry-Galerie” sagt, entsteht oft eine Demo, aber keine belastbare Produktionslösung. Die kritischen Punkte sind Bildgewicht, alt-Text, Layout Shift, mobile Breite, Tastaturbedienung, fehlerhafte CMS-Daten und der nächste Schritt nach dem Bild.
Dieser Guide behandelt die Galerie als veröffentlichbare UI. Wir geben Claude Code klare Grenzen, bauen eine typisierte React-Komponente, nutzen srcset, sizes, Lazy Loading und Lightbox, und prüfen typische Fehler vor dem Rollout. Ergänzend passen Claude Code Bildverarbeitung, Performance-Optimierung und Accessibility-Implementierung. Als externe Referenzen nutze die Claude Code Dokumentation, MDN zu responsive images, MDN zu Lazy loading und WCAG 2.2.
Mein praktischer Startpunkt ist der Datenvertrag. Jede Bildzeile braucht id, Kategorie, Breite, Höhe und einen sinnvollen Alternativtext. Danach kann das Team immer noch entscheiden, ob rohe img-Tags, Next.js Image, Astro Assets oder ein CDN genutzt werden.
Claude Code mit Grenzen beauftragen
Dieser Prompt ist bewusst konkret. Er fordert lauffähigen Code, Fehlerzustände und eine Review-Antwort.
Implementiere eine React-Bildergalerie.
Ziel ist eine schnelle UI für Artikel, Case Studies, Produkt-Screenshots und Workshop-Fotos.
Bedingungen:
- Bestehendes Routing und Design-System-Konventionen nicht beschädigen.
- Einen Bildtyp mit id, src, alt, width, height und category als Pflichtfelder definieren.
- CSS Grid für das responsive Layout verwenden.
- srcset, sizes, loading und fetchPriority bewusst einsetzen.
- Per Klick eine Lightbox öffnen und mit Escape schließen.
- Leere Arrays, Bildfehler, lange alt-Texte und mobile Breite behandeln.
- Danach geänderte Dateien, Tests und Restrisiken erklären.
Gib kopierbares React/TypeScript und CSS zurück, keinen Pseudocode.
Damit verschiebt sich die Ausgabe von “schöne Vorschau” zu “kleiner prüfbarer Patch”. width und height reduzieren Layout Shift. Pflicht-alt verhindert leere Beschreibungen. Die Risikofrage zwingt Claude Code zu einer zweiten Review-Perspektive.
Empfohlene Struktur
Vor dem Code hilft ein einfaches Ablaufbild. Claude Code trennt Verantwortlichkeiten besser, wenn Daten, UI und Prüfung sichtbar sind.
flowchart LR
A["Originalbilder"] --> B["Größenvarianten"]
B --> C["GalleryImage Array"]
C --> D["Kategorie-Filter"]
D --> E["CSS Grid Karten"]
E --> F["Lightbox"]
E --> G["Lighthouse und manuelle Prüfung"]
| Entscheidung | Sicherer Start | Neu bewerten wenn |
|---|---|---|
| Layout | CSS Grid | Bildhöhen stark variieren |
| Lazy Loading | Unterhalb des ersten Viewports | Das Hauptbild zu spät erscheint |
| Varianten | Etwa 480/960/1440px | Viele große Displays im Analytics auftauchen |
| Lightbox | Minimale zugängliche Version | Die Galerie Kaufentscheidungen beeinflusst |
Eine Masonry-Library ist nicht automatisch nötig. Viele Seiten brauchen stabile Karten, schnelle Thumbnails und eine klare Vergrößerung. Weniger Abhängigkeiten machen den von Claude Code erzeugten Diff außerdem leichter prüfbar.
Kopierbare React-Implementierung
Die Komponente ist bewusst framework-arm. Sie funktioniert in Vite, kann in eine Next.js Client Component wandern und später an vorhandene Bildabstraktionen angepasst werden.
import { useEffect, useMemo, useState } from "react";
import "./image-gallery.css";
export type GalleryImage = {
id: string;
src: string;
alt: string;
width: number;
height: number;
category: string;
sources?: Array<{ width: number; src: string }>;
};
function buildSrcSet(image: GalleryImage) {
if (!image.sources?.length) return undefined;
return [...image.sources]
.sort((a, b) => a.width - b.width)
.map((source) => `${source.src} ${source.width}w`)
.join(", ");
}
export function ImageGallery({ images }: { images: GalleryImage[] }) {
const [category, setCategory] = useState("all");
const [activeId, setActiveId] = useState<string | null>(null);
const [brokenIds, setBrokenIds] = useState<Set<string>>(() => new Set());
const categories = useMemo(() => {
return ["all", ...Array.from(new Set(images.map((image) => image.category)))];
}, [images]);
const visibleImages = useMemo(() => {
if (category === "all") return images;
return images.filter((image) => image.category === category);
}, [category, images]);
const activeImage = visibleImages.find((image) => image.id === activeId);
useEffect(() => {
if (!activeImage) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") setActiveId(null);
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [activeImage]);
function markBroken(id: string) {
setBrokenIds((current) => new Set(current).add(id));
}
if (images.length === 0) {
return <p className="gallery-empty">No images are available yet.</p>;
}
return (
<section className="gallery" aria-label="Image gallery">
<div className="gallery-toolbar" aria-label="Filter images by category">
{categories.map((item) => (
<button
className={item === category ? "is-active" : ""}
key={item}
onClick={() => setCategory(item)}
type="button"
>
{item === "all" ? "All" : item}
</button>
))}
</div>
<div className="gallery-grid">
{visibleImages.map((image, index) => {
const isBroken = brokenIds.has(image.id);
return (
<button
className="gallery-card"
key={image.id}
onClick={() => setActiveId(image.id)}
type="button"
>
{isBroken ? (
<span className="gallery-fallback">Image unavailable</span>
) : (
<img
alt={image.alt}
width={image.width}
height={image.height}
src={image.src}
srcSet={buildSrcSet(image)}
sizes="(min-width: 960px) 33vw, (min-width: 640px) 50vw, 100vw"
loading={index < 2 ? "eager" : "lazy"}
fetchPriority={index === 0 ? "high" : "auto"}
style={{ aspectRatio: `${image.width} / ${image.height}` }}
onError={() => markBroken(image.id)}
/>
)}
<span>{image.alt}</span>
</button>
);
})}
</div>
{activeImage && (
<div
className="gallery-lightbox"
role="dialog"
aria-modal="true"
aria-label={activeImage.alt}
tabIndex={-1}
onClick={() => setActiveId(null)}
>
<button className="gallery-close" onClick={() => setActiveId(null)} type="button">
Close
</button>
<img
alt={activeImage.alt}
width={activeImage.width}
height={activeImage.height}
src={activeImage.src}
onClick={(event) => event.stopPropagation()}
/>
</div>
)}
</section>
);
}
.gallery {
display: grid;
gap: 1rem;
}
.gallery-toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.gallery-toolbar button,
.gallery-card,
.gallery-close {
border: 1px solid #d4d4d8;
background: #ffffff;
color: #18181b;
cursor: pointer;
}
.gallery-toolbar button {
border-radius: 999px;
padding: 0.45rem 0.8rem;
}
.gallery-toolbar .is-active {
background: #18181b;
color: #ffffff;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
.gallery-card {
display: grid;
gap: 0.5rem;
padding: 0;
overflow: hidden;
border-radius: 8px;
text-align: left;
}
.gallery-card img {
width: 100%;
object-fit: cover;
background: #f4f4f5;
}
.gallery-fallback {
display: grid;
min-height: 180px;
place-items: center;
background: #f4f4f5;
color: #71717a;
}
.gallery-card span {
padding: 0 0.75rem 0.75rem;
font-size: 0.875rem;
}
.gallery-lightbox {
position: fixed;
inset: 0;
z-index: 50;
display: grid;
place-items: center;
padding: 2rem;
background: rgb(0 0 0 / 0.86);
}
.gallery-lightbox img {
max-width: min(100%, 1100px);
max-height: 82vh;
object-fit: contain;
}
.gallery-close {
position: absolute;
top: 1rem;
right: 1rem;
border-radius: 6px;
padding: 0.5rem 0.75rem;
}
.gallery-empty {
color: #71717a;
}
In einem echten Projekt kann das rohe img durch die Standard-Bildkomponente ersetzt werden. Wichtig bleibt der Datenvertrag mit alt, width, height und sizes.
Datenmodell und Use Cases
Halte die Daten außerhalb der Komponente. Auch CMS-Daten sollten vor dem Rendern normalisiert werden.
import type { GalleryImage } from "./ImageGallery";
export const galleryImages: GalleryImage[] = [
{
id: "case-study-dashboard",
src: "/images/gallery/dashboard-960.webp",
alt: "Analytics dashboard after Claude Code refactoring",
width: 960,
height: 640,
category: "Case study",
sources: [
{ width: 480, src: "/images/gallery/dashboard-480.webp" },
{ width: 960, src: "/images/gallery/dashboard-960.webp" },
{ width: 1440, src: "/images/gallery/dashboard-1440.webp" },
],
},
{
id: "workshop-room",
src: "/images/gallery/workshop-960.webp",
alt: "Team workshop board with Claude Code review checklist",
width: 960,
height: 720,
category: "Training",
},
{
id: "product-shot",
src: "/images/gallery/template-pack-960.webp",
alt: "Claude Code template pack product preview",
width: 960,
height: 540,
category: "Product",
},
];
Use Case eins ist ein Portfolio oder eine Case-Study-Seite. Bilder sollen Ergebnisse vergleichbar machen und dann zu Artikel, Kontakt oder Beratung führen.
Use Case zwei ist Ecommerce oder ein digitales Produkt. Screenshots, Nutzungsszenen und Vergleichsbilder nehmen Unsicherheit. Alle hochauflösenden Bilder vor dem CTA zu laden, ist dagegen teuer.
Use Case drei sind Schulung, Event und internes Wissen. Whiteboards, Schrittbilder, Vorher/Nachher und Fehlerscreens werden wiederverwendbares Material. Intern müssen Kundennamen, E-Mails und Secrets geprüft werden.
Use Case vier sind technische Artikel. Bei mehreren Codebeispielen helfen Diagramme und Prüfscreenshots, damit Leser den Kontext behalten.
Typische Fehler
Der häufigste Fehler ist Lazy Loading für das zuerst sichtbare Bild. Dieses Bild kann LCP beeinflussen, daher sind eager und fetchPriority="high" für das erste Element oft sinnvoll. Der Gegenfehler ist, alles sofort zu laden.
Der zweite Fehler ist das Weglassen von width und height. Karten ändern dann beim Laden ihre Höhe und die Seite wirkt instabil. Bitte Claude Code um eine explizite CLS-Prüfung.
Der dritte Fehler ist SEO-Stuffing im alt-Text. Alt soll die Bildaussage ersetzen, wenn das Bild nicht gesehen werden kann.
Der vierte Fehler ist eine Maus-only-Lightbox. Ein benannter Schließen-Button, Escape, sichtbarer Fokus und mobile Nutzbarkeit gehören zum Minimum. Für striktes Focus Trapping sind Radix UI oder React Aria oft sicherer.
Der fünfte Fehler sind fehlende Betriebsregeln. Ein 6-MB-PNG aus dem CMS kann die Seite ruinieren. Maximale Größe, Formate, Namensregeln und Review-Punkte gehören in CLAUDE.md.
Prüfung vor der Veröffentlichung
Prüfe Filter, Lightbox, Tastatur, leere Daten, kaputte Bilder und 375px Breite. Mit Playwright reicht zum Start ein kleiner Test:
import { expect, test } from "@playwright/test";
test("image gallery filters and opens a lightbox", async ({ page }) => {
await page.goto("/gallery");
await expect(page.getByRole("region", { name: "Image gallery" })).toBeVisible();
await page.getByRole("button", { name: "Training" }).click();
await expect(page.getByRole("button", { name: /workshop/i })).toBeVisible();
await page.getByRole("button", { name: /workshop/i }).click();
await expect(page.getByRole("dialog")).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.getByRole("dialog")).toBeHidden();
});
Die Review-Frage sollte konkret sein: initiale Bildrequests, Abgleich von srcset und sizes, Qualität des Alt-Texts, Button- oder Link-Rollen, mobile Overflows, defekte CMS-Daten und private Informationen in Screenshots.
CTA und praktisches Ergebnis
Eine Galerie sollte einen Geschäftspfad unterstützen. Case-Bilder führen zum Case, Produktbilder zu Kaufdetails, Workshop-Fotos zur Claude Code Schulung und Beratung. Technisch passen danach Lazy Loading Images und React-Entwicklung mit Claude Code.
Beim Testen dieses Ablaufs war die Trennung von Datenvertrag, Komponente, CSS und Review deutlich leichter zu prüfen als ein großer Design-Prompt. Pflichtfelder für width, height und alt haben schwache Daten früh sichtbar gemacht. Die letzte Runde nutzte DevTools Network, Lighthouse und einen mobilen manuellen Check.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Workflow von Obsidian zu CLAUDE.md
Obsidian-Arbeitsnotizen in CLAUDE.md-Betriebsnotizen verwandeln und Kontext nicht ständig neu erklären.
Claude Code Revenue CTA Routing: Artikel zu PDF, Gumroad und Beratung führen
Ein Claude-Code-Ablauf, der Leser nach Absicht zu Gratis-PDF, Gumroad oder Beratung führt.
Claude-Code-Team-Handoff-Regeln: Belege, Berechtigungen, Rollback und Umsatzpfade
Ein praktisches Claude-Code-Handoff für Review-Belege, Berechtigungen, Rollback, Gratis-PDF, Gumroad und Beratung.