JavaScript-Grundlagen III: Karten, GeoJSON & Dashboards
1. Karten im Web
Interaktive Karten gehören zu den wirkungsvollsten Visualisierungsformen in den Digital Humanities. Sie machen räumliche Zusammenhänge auf einen Blick sichtbar, die in Tabellen oder Listen verborgen bleiben. Ein Beispiel: Die Korrespondenz von Hugo Schuchardt umfasst Briefe aus über 50 Städten in Europa — auf einer Karte wird sein wissenschaftliches Netzwerk sofort greifbar.
Warum Karten für DH-Projekte?
- Räumliche Muster: Wo konzentrieren sich Korrespondenzen, Fundorte, Ereignisse?
- Netzwerke: Verbindungen zwischen Orten, Personen und Institutionen
- Zeitliche Entwicklung: Wie verändert sich die räumliche Verteilung über die Zeit?
- Exploration: Nutzende können selbst durch die Daten navigieren
Leaflet vs. OpenLayers
Die beiden bekanntesten Open-Source-Bibliotheken für Karten im Web sind Leaflet.js und OpenLayers. Für die meisten DH-Projekte ist Leaflet die bessere Wahl:
| Kriterium | Leaflet.js | OpenLayers |
|---|---|---|
| Dateigröße | ~40 KB (gzipped) | ~180 KB (gzipped) |
| Lernkurve | Flach, einfache API | Steiler, umfangreicher |
| GeoJSON | Nativ unterstützt | Nativ unterstützt |
| Plugins | Grosses Ökosystem | Weniger Plugins |
| Ideal für | Einfache bis mittlere Karten | Komplexe GIS-Anwendungen |
Tile-Server und Kartenkacheln
Weder Leaflet noch OpenLayers enthalten eigene Kartendaten. Sie laden Kacheln (Tiles) von einem Tile-Server. Das sind kleine Bildausschnitte (256×256 Pixel), die je nach Zoom-Stufe und Position zusammengesetzt werden. Die bekanntesten kostenlosen Tile-Provider sind:
- OpenStreetMap: Detailliert, farbig, Community-gepflegt
- CartoDB Positron: Hell, minimalistisch — ideal für Datenvisualisierung
- CartoDB Dark Matter: Dunkel — gut für leuchtende Marker
- Stamen Toner: Schwarzweiss — gut für Druckansichten
Tipp
Für DH-Projekte empfehlen wir CartoDB Positron als Basis-Karte. Die helle, dezente Gestaltung lenkt nicht von den eigentlichen Daten ab und eignet sich hervorragend für Marker, Polygone und Heatmaps.
2. Leaflet.js Grundlagen
Installation per CDN
Leaflet wird über ein CDN (Content Delivery Network) eingebunden. Zwei Zeilen
im <head> Ihrer HTML-Datei genügen:
<!-- Leaflet CSS (muss vor dem JS geladen werden) -->
<link rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin="" />
<!-- Leaflet JavaScript -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
Schlüsselkonzept: CDN-Einbindung
Ein CDN liefert Dateien von Servern weltweit aus, die dem Nutzenden geographisch
nahe sind. Das integrity-Attribut (Subresource Integrity, SRI) stellt
sicher, dass die geladene Datei nicht manipuliert wurde. Das crossorigin-Attribut
erlaubt dem Browser, die Integritätsprüfung durchzuführen.
Eine Karte erstellen
Jede Leaflet-Karte braucht einen HTML-Container mit einer festen Höhe und eine JavaScript-Initialisierung:
<!-- HTML: Container mit fester Hoehe -->
<div id="map" style="height: 500px;"></div>
<script>
// Karte initialisieren und auf Graz zentrieren
const map = L.map('map').setView([47.07, 15.44], 13);
// OpenStreetMap-Kacheln laden
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
</script>
Die drei wichtigsten Methoden bei der Initialisierung:
L.map('map')— erstellt die Karte im Container mit der IDmap.setView([lat, lng], zoom)— setzt Mittelpunkt und Zoom-StufeL.tileLayer(url, options).addTo(map)— lädt die Kartenkacheln
Häufiger Fehler
Wenn die Karte nicht angezeigt wird, fehlt meist die Höhe des Containers.
Ohne height hat der <div> eine Höhe von 0 Pixeln,
und die Karte ist unsichtbar. Setzen Sie immer eine feste Höhe oder verwenden Sie
CSS (height: 100vh; für Vollbild).
Marker
Marker kennzeichnen einzelne Punkte auf der Karte. Sie werden mit Koordinaten erstellt und zur Karte hinzugefügt:
// Einfacher Marker
L.marker([47.0787, 15.4485]).addTo(map);
// Marker mit Popup (oeffnet bei Klick)
L.marker([47.0787, 15.4485])
.addTo(map)
.bindPopup('<strong>Universität Graz</strong><br>Hauptgebäude, Universitätsplatz 3');
// Marker mit sofort offenem Popup
L.marker([47.0787, 15.4485])
.addTo(map)
.bindPopup('Uni Graz')
.openPopup();
Popups und Tooltips
Popups öffnen sich bei Klick und können HTML enthalten. Tooltips erscheinen beim Hover und sind für kurze Beschriftungen gedacht:
// Popup mit HTML-Inhalt
marker.bindPopup(`
<h3>Landesarchiv Steiermark</h3>
<p>Karmeliterplatz 3, 8010 Graz</p>
<p><em>Bestände: Urkunden ab dem 12. Jh.</em></p>
`);
// Tooltip (erscheint bei Hover)
marker.bindTooltip('Landesarchiv');
Kreise und Polygone
Neben Markern unterstützt Leaflet auch geometrische Formen:
// Kreis mit Radius in Metern
L.circle([47.07, 15.44], {
radius: 500,
color: '#1a5276',
fillColor: '#2980b9',
fillOpacity: 0.3
}).addTo(map);
// Polygon (Dreieck)
L.polygon([
[47.08, 15.43],
[47.06, 15.43],
[47.07, 15.46]
], {
color: '#C0392B',
fillOpacity: 0.2
}).addTo(map);
Ausprobieren
Öffnen Sie die Beispieldatei examples/01-leaflet-karte.html im
Browser und experimentieren Sie mit den Koordinaten, der Zoom-Stufe und den
Marker-Texten. Ändern Sie den Tile-Provider zu CartoDB Positron und beobachten
Sie den Unterschied.
3. GeoJSON verstehen
GeoJSON (RFC 7946) ist ein offenes Datenformat für geographische Strukturen, das auf JSON basiert. Es ist der Standard für räumliche Daten im Web und wird von praktisch allen Kartenbibliotheken und GIS-Tools unterstützt.
Grundstruktur
Die beiden wichtigsten Typen in GeoJSON sind Feature und FeatureCollection:
// FeatureCollection: Die haeufigste Struktur
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [15.4485, 47.0787]
},
"properties": {
"name": "Graz",
"land": "Österreich",
"briefanzahl": 342
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [16.3738, 48.2082]
},
"properties": {
"name": "Wien",
"land": "Österreich",
"briefanzahl": 218
}
}
]
}
Schlüsselkonzept: Koordinatenreihenfolge
GeoJSON verwendet die Reihenfolge [Längengrad, Breitengrad]
(also [lng, lat]), während Leaflet [lat, lng]
erwartet. Leaflet konvertiert GeoJSON-Koordinaten automatisch — aber wenn
Sie manuell Koordinaten angeben, müssen Sie die richtige Reihenfolge beachten.
Geometrietypen
GeoJSON unterstützt verschiedene Geometrietypen:
- Point: Ein einzelner Punkt — z.B. ein Ort, ein Fundort
- LineString: Eine Linie aus mehreren Punkten — z.B. eine Reiseroute
- Polygon: Eine geschlossene Fläche — z.B. eine Region, ein Gelände
- MultiPoint: Mehrere Punkte als ein Feature
- MultiLineString: Mehrere Linien als ein Feature
- MultiPolygon: Mehrere Flächen als ein Feature
// LineString: Reiseroute Graz - Wien - Berlin
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[15.44, 47.07],
[16.37, 48.21],
[13.41, 52.52]
]
},
"properties": {
"name": "Reiseroute Schuchardt 1887",
"zweck": "Vortragsreise"
}
}
// Polygon: Steiermark (vereinfacht)
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[14.3, 46.6], [16.2, 46.6],
[16.2, 47.8], [14.3, 47.8],
[14.3, 46.6]
]]
},
"properties": {
"name": "Steiermark",
"typ": "Bundesland"
}
}
Properties
Das properties-Objekt eines Features kann beliebige Metadaten enthalten.
Es gibt keine vorgegebene Struktur — Sie können jede Information speichern,
die für Ihr Projekt relevant ist:
"properties": {
"name": "Graz",
"institution": "Universität Graz",
"briefanzahl": 342,
"zeitraum": "1876-1927",
"partner": ["Ascoli", "Meyer", "Baudouin"],
"url": "https://schuchardt.uni-graz.at"
}
Hinweis
GeoJSON-Dateien haben die Endung .geojson oder .json.
Sie können GeoJSON in Online-Tools wie
geojson.io erstellen und validieren.
4. GeoJSON mit Leaflet
Leaflet hat erstklassige Unterstützung für GeoJSON. Die Grundfunktion ist denkbar einfach:
// GeoJSON-Daten zur Karte hinzufuegen
L.geoJSON(geojsonData).addTo(map);
Leaflet erkennt automatisch den Geometrietyp und erstellt die passenden Layer: Marker für Points, Polylines für LineStrings, Polygone für Polygons.
Styling von GeoJSON
Mit der style-Option können Sie das Aussehen von Linien und
Flächen anpassen:
L.geoJSON(geojsonData, {
style: (feature) => {
return {
color: '#1a5276',
weight: 2,
fillColor: '#2980b9',
fillOpacity: 0.3
};
}
}).addTo(map);
Die style-Funktion erhält das Feature als Parameter, sodass
Sie das Styling von den Properties abhängig machen können:
// Farbe abhaengig von der Briefanzahl
L.geoJSON(geojsonData, {
style: (feature) => {
const briefe = feature.properties.briefanzahl;
let farbe = '#aaa';
if (briefe > 200) farbe = '#C0392B';
else if (briefe > 100) farbe = '#D4AC0D';
else if (briefe > 50) farbe = '#27AE60';
return { color: farbe, weight: 2 };
}
}).addTo(map);
onEachFeature: Popups und Events
Die onEachFeature-Callback-Funktion wird für jedes Feature einmal
aufgerufen. Sie erhält das Feature (mit seinen Properties) und den Layer
(das Leaflet-Objekt):
const onEachFeature = (feature, layer) => {
if (feature.properties && feature.properties.name) {
layer.bindPopup(`
<strong>${feature.properties.name}</strong><br>
Briefe: ${feature.properties.briefanzahl || 0}<br>
Zeitraum: ${feature.properties.zeitraum || 'unbekannt'}
`);
}
};
L.geoJSON(geojsonData, {
onEachFeature: onEachFeature
}).addTo(map);
pointToLayer: Marker anpassen
Standardmäßig erstellt Leaflet für Point-Features blaue Standard-Marker.
Mit pointToLayer können Sie stattdessen Circle-Marker verwenden,
deren Größe von den Daten abhängt:
L.geoJSON(geojsonData, {
pointToLayer: (feature, latlng) => {
const briefe = feature.properties.briefanzahl || 1;
const radius = Math.sqrt(briefe) * 2; // Groesse proportional
return L.circleMarker(latlng, {
radius: radius,
fillColor: '#1a5276',
color: '#fff',
weight: 1,
fillOpacity: 0.7
});
},
onEachFeature: onEachFeature
}).addTo(map);
Schlüsselkonzept: L.geoJSON Optionen
Die drei wichtigsten Optionen für L.geoJSON() sind:
style— Aussehen von Linien und FlächenonEachFeature— Popups, Tooltips und Events für jedes FeaturepointToLayer— Marker-Typ und -Größe für Point-Features
Ausprobieren
Öffnen Sie examples/02-geojson-karte.html und ändern Sie
die Properties eines Features. Beobachten Sie, wie sich Popup-Inhalt und
Marker-Größe ändern.
5. Interaktive Karten
Click-Events auf Features
Leaflet-Layer unterstützen verschiedene Events. Das wichtigste ist click:
L.geoJSON(geojsonData, {
onEachFeature: (feature, layer) => {
layer.on('click', (e) => {
console.log('Geklickt auf:', feature.properties.name);
console.log('Koordinaten:', e.latlng);
});
}
}).addTo(map);
Weitere nützliche Events:
mouseover/mouseout— Hover-Effektedblclick— Doppelklickcontextmenu— Rechtsklick
Sidebar/Panel aktualisieren
Das typische Muster für interaktive DH-Anwendungen: Klick auf der Karte aktualisiert einen Inhaltsbereich neben der Karte.
const detailPanel = document.querySelector('#detail-panel');
L.geoJSON(geojsonData, {
onEachFeature: (feature, layer) => {
layer.bindPopup(feature.properties.name);
layer.on('click', () => {
const p = feature.properties;
detailPanel.innerHTML = `
<h3>${p.name}</h3>
<table>
<tr><th>Briefe</th><td>${p.briefanzahl}</td></tr>
<tr><th>Zeitraum</th><td>${p.zeitraum}</td></tr>
<tr><th>Partner</th><td>${(p.partner || []).join(', ')}</td></tr>
</table>
`;
});
}
}).addTo(map);
Marker filtern
Um Marker dynamisch ein- und auszublenden, speichern Sie die GeoJSON-Layer-Gruppe und erstellen sie bei Bedarf neu:
let geojsonLayer = null;
const renderMarker = (daten) => {
// Alte Marker entfernen
if (geojsonLayer) {
map.removeLayer(geojsonLayer);
}
// Neue Marker erstellen
geojsonLayer = L.geoJSON(daten, {
onEachFeature: onEachFeature
}).addTo(map);
// Kartenausschnitt anpassen
if (geojsonLayer.getBounds().isValid()) {
map.fitBounds(geojsonLayer.getBounds());
}
};
// Filter-Input
const suchfeld = document.querySelector('#suche');
suchfeld.addEventListener('input', (e) => {
const suchbegriff = e.target.value.toLowerCase();
const gefiltert = {
type: 'FeatureCollection',
features: geojsonData.features.filter(f =>
f.properties.name.toLowerCase().includes(suchbegriff)
)
};
renderMarker(gefiltert);
});
Tipp
map.fitBounds(layer.getBounds()) passt den Kartenausschnitt automatisch
so an, dass alle Marker sichtbar sind. Das ist besonders nützlich nach dem
Filtern, wenn nur noch wenige Marker übrig sind.
6. Einfache Datenvisualisierung ohne Bibliothek
Nicht jede Visualisierung braucht eine mächtige Bibliothek wie D3.js oder Chart.js. Für einfache Balkendiagramme und Zeitleisten reichen CSS und SVG vollkommen aus.
CSS Bar Chart
Die Idee: Ein Flex-Container mit align-items: flex-end richtet
Kind-Elemente am unteren Rand aus. Die Höhe jedes Balkens wird als
Prozent des Maximalwerts berechnet.
// Daten
const daten = [
{ label: '1870er', wert: 45 },
{ label: '1880er', wert: 187 },
{ label: '1890er', wert: 234 },
{ label: '1900er', wert: 156 }
];
// Maximalwert fuer proportionale Berechnung
const maxWert = Math.max(...daten.map(d => d.wert));
// Balken generieren
const chart = document.querySelector('#chart');
daten.forEach(d => {
const prozent = (d.wert / maxWert) * 100;
chart.innerHTML += `
<div class="bar" style="height: ${prozent}%">
<span class="bar__wert">${d.wert}</span>
<span class="bar__label">${d.label}</span>
</div>
`;
});
/* CSS fuer den Bar Chart */
#chart {
display: flex;
align-items: flex-end;
gap: 0.5rem;
height: 250px;
border-bottom: 2px solid #333;
border-left: 2px solid #333;
padding: 0 1rem 0 0.5rem;
}
.bar {
flex: 1;
background: linear-gradient(to top, #1a5276, #2980b9);
border-radius: 4px 4px 0 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding-top: 0.5rem;
position: relative;
min-width: 40px;
transition: height 0.5s ease;
}
.bar__wert {
color: white;
font-size: 0.85rem;
font-weight: bold;
}
.bar__label {
position: absolute;
bottom: -1.8rem;
font-size: 0.75rem;
color: #666;
white-space: nowrap;
}
SVG-Grundlagen für Visualisierung
SVG (Scalable Vector Graphics) bietet mehr Kontrolle als CSS. Die wichtigsten Elemente für Datenvisualisierung:
<rect>— Rechtecke für Balkendiagramme<circle>— Kreise für Datenpunkte<line>— Linien für Achsen und Verbindungen<text>— Beschriftungen<g>— Gruppen für zusammengehörige Elemente
// SVG Bar Chart generieren
const svgBreite = 500, svgHoehe = 250;
const maxWert = Math.max(...daten.map(d => d.wert));
const barBreite = (svgBreite / daten.length) - 15;
const padding = 30;
let bars = '';
daten.forEach((d, i) => {
const barHoehe = (d.wert / maxWert) * (svgHoehe - padding * 2);
const x = i * (barBreite + 15) + padding;
const y = svgHoehe - barHoehe - padding;
bars += `
<rect x="${x}" y="${y}" width="${barBreite}" height="${barHoehe}"
fill="#1a5276" rx="3" />
<text x="${x + barBreite / 2}" y="${y - 8}"
text-anchor="middle" font-size="13" fill="#333">${d.wert}</text>
<text x="${x + barBreite / 2}" y="${svgHoehe - 8}"
text-anchor="middle" font-size="12" fill="#666">${d.label}</text>
`;
});
document.querySelector('#svg-chart').innerHTML = `
<svg width="${svgBreite}" height="${svgHoehe}" viewBox="0 0 ${svgBreite} ${svgHoehe}">
<line x1="${padding}" y1="${svgHoehe - padding}"
x2="${svgBreite}" y2="${svgHoehe - padding}"
stroke="#333" stroke-width="2" />
${bars}
</svg>
`;
Canvas (Ausblick)
Für sehr grosse Datenmengen (tausende Datenpunkte) bietet HTML5 Canvas eine performantere Alternative zu SVG. Canvas zeichnet Pixel statt DOM-Elemente und ist daher schneller bei vielen Objekten. Für die meisten DH-Projekte reicht SVG aber völlig aus.
Ausprobieren
Öffnen Sie examples/03-daten-visualisierung.html und
experimentieren Sie mit den Datenwerten. Ändern Sie Zahlen, fügen Sie
neue Jahrzehnte hinzu und beobachten Sie, wie sich das Diagramm anpasst.
7. Dashboard-Aufbau
Ein Dashboard kombiniert mehrere Darstellungsformen zu einer integrierten Ansicht. In DH-Projekten ist ein typisches Dashboard: Karte + Tabelle + Filter.
CSS Grid für Multi-Panel-Layouts
CSS Grid eignet sich hervorragend für Dashboard-Layouts, weil man Bereiche benennen und visuell anordnen kann:
/* Dashboard-Layout mit benannten Bereichen */
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto 1fr;
grid-template-areas:
"filter filter"
"karte tabelle";
gap: 1rem;
height: calc(100vh - 80px);
padding: 1rem;
}
.dashboard__filter { grid-area: filter; }
.dashboard__karte { grid-area: karte; }
.dashboard__tabelle {
grid-area: tabelle;
overflow-y: auto;
}
Die grid-template-areas-Eigenschaft macht das Layout im Code lesbar:
Man sieht sofort, dass der Filter oben über die gesamte Breite geht,
und Karte und Tabelle nebeneinander stehen.
Responsive Dashboard
Auf kleinen Bildschirmen sollten die Panels untereinander stehen:
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
grid-template-rows: auto auto 1fr;
grid-template-areas:
"filter"
"karte"
"tabelle";
height: auto;
}
.dashboard__karte {
min-height: 300px;
}
}
Komponenten verbinden
Das zentrale Prinzip eines Dashboards: eine Datenquelle, mehrere Darstellungen. Wenn der Filter geändert wird, aktualisieren sich alle Komponenten:
// Zentrale Render-Funktion
const renderAll = (daten) => {
renderKarte(daten);
renderTabelle(daten);
};
// Filter-Event
document.querySelector('#suche').addEventListener('input', (e) => {
const term = e.target.value.toLowerCase();
const gefiltert = alleDaten.filter(d =>
d.name.toLowerCase().includes(term) ||
d.partner.toLowerCase().includes(term)
);
renderAll(gefiltert);
});
// Karte rendern
const renderKarte = (daten) => {
if (markerLayer) map.removeLayer(markerLayer);
const geojson = {
type: 'FeatureCollection',
features: daten.map(d => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [d.lng, d.lat] },
properties: d
}))
};
markerLayer = L.geoJSON(geojson, { onEachFeature }).addTo(map);
};
// Tabelle rendern
const renderTabelle = (daten) => {
const tbody = document.querySelector('#tabelle tbody');
tbody.innerHTML = daten.map(d => `
<tr>
<td>${d.name}</td>
<td>${d.partner}</td>
<td>${d.briefe}</td>
</tr>
`).join('');
};
Schlüsselkonzept: Single Source of Truth
In einem Dashboard gibt es genau eine Datenquelle (das Array mit allen Einträgen) und mehrere Views (Karte, Tabelle, Chart). Filter ändern nicht die Views direkt, sondern filtern die Daten, und alle Views werden mit den gefilterten Daten neu gerendert.
Performance-Hinweis
Bei grossen Datenmengen (mehr als 500 Marker) kann das komplette Neurendern bei jedem Tastendruck langsam werden. Lösungen: Debouncing (nur nach einer kurzen Pause rendern) oder Marker-Clustering (viele nahe Marker zu einem Cluster zusammenfassen).
8. CLAUDE.md und Projektstruktur
Wenn Sie mit einem LLM wie Claude an einem Projekt arbeiten, ist es entscheidend, dass das LLM den Kontext Ihres Projekts versteht. Die CLAUDE.md-Datei ist dafür das zentrale Werkzeug: Sie wird im Stammverzeichnis Ihres Projekts abgelegt und von Claude Code automatisch gelesen.
Was gehört in eine CLAUDE.md?
# CLAUDE.md
## Projektbeschreibung
Kurze Beschreibung: Was ist das Projekt? Fuer wen?
## Technische Regeln
- Welche Technologien? (z.B. Vanilla HTML/CSS/JS, kein Framework)
- Externe Abhaengigkeiten? (z.B. Leaflet.js per CDN)
- Welche Browser? (z.B. aktuelle Chrome/Firefox/Safari)
- Pfad-Konventionen? (z.B. alle Pfade relativ)
## Dateistruktur
- Welche Ordner gibt es?
- Wo liegen HTML, CSS, JS, Daten?
- Wie heissen die wichtigsten Dateien?
## Konventionen
- Sprache der Inhalte (z.B. Deutsch)
- Variablen-Stil (z.B. const/let, kein var)
- CSS-Klassen-Konvention (z.B. BEM)
- HTML-Stil (z.B. semantische Elemente)
## Datenformate
- Beispiel-Strukturen fuer JSON, GeoJSON etc.
- Welche Felder haben die Datenobjekte?
Dateien und Ordner sinnvoll strukturieren
Eine klare Projektstruktur hilft nicht nur dem LLM, sondern auch Ihnen selbst:
mein-dh-portfolio/
index.html # Startseite
karte.html # Korrespondenz-Karte
tabelle.html # Briefe-Tabelle
dashboard.html # Kombinierte Ansicht
css/
style.css # Alle Styles
js/
app.js # Hauptlogik
karte.js # Karten-spezifischer Code
data/
briefe.json # Brief-Metadaten
orte.geojson # Geographische Daten
img/
logo.svg # Projektlogo
CLAUDE.md # Projektregeln fuer LLM
Namenskonventionen
- Dateinamen: Kleinbuchstaben, Bindestriche statt Leerzeichen (
mein-projekt.html) - CSS-Klassen: BEM-artig (
.dashboard__filter,.bar-chart__label) - JavaScript-Variablen: camelCase (
geojsonDaten,renderTabelle) - Daten-Dateien: Beschreibend (
briefe.json, nichtdata.json)
Beispiel-Prompt mit CLAUDE.md-Bezug
„Lies die CLAUDE.md und erstelle eine neue Seite dashboard.html, die eine Leaflet-Karte links und eine Tabelle rechts zeigt. Verwende die Daten aus data/briefe.json. Halte dich an die CSS-Konventionen aus css/style.css.“
9. Context Engineering für komplexe Projekte
Context Engineering (CE) ist die Kunst, einem LLM genau den Kontext zu geben, den es braucht, um konsistenten und korrekten Code zu generieren. Bei komplexen Projekten mit mehreren Dateien ist das besonders wichtig.
Was ist Kontext?
Kontext umfasst alles, was das LLM wissen muss, um guten Code zu generieren:
- Projektstruktur: Welche Dateien gibt es? Wie hängen sie zusammen?
- Bestehender Code: Wie sieht das vorhandene CSS aus? Welche JavaScript-Funktionen gibt es schon?
- Datenbeispiele: Wie sehen die JSON- oder GeoJSON-Daten aus?
- Design-Tokens: Welche Farben, Schriften und Abstände werden verwendet?
- Konventionen: Welcher Coding-Stil wird verwendet?
Kontext-Strategien
CLAUDE.md als Basis
Die CLAUDE.md wird automatisch gelesen und gibt dem LLM die Grundregeln. Sie müssen sie nicht in jedem Prompt wiederholen.
Relevante Dateien referenzieren
Sagen Sie dem LLM, welche bestehenden Dateien es berücksichtigen soll: „Orientiere dich am Stil von karte.html und verwende die Klassen aus css/style.css.“
Datenbeispiele mitliefern
Zeigen Sie dem LLM, wie die Daten aussehen. Ein Beispiel-JSON mit 2–3 Einträgen reicht, damit das LLM die Struktur versteht.
Anforderungen präzisieren (RE)
Beschreiben Sie genau, was die Komponente tun soll, für wen sie ist und welche Interaktionen möglich sein sollen.
Ergebnis prüfen (RV)
Testen Sie den generierten Code im Browser. Prüfen Sie: Funktioniert alles? Stimmen die Daten? Ist der Code lesbar und konsistent?
Beispiel: Guter Prompt mit Kontext
„Ich arbeite an einem DH-Portfolio (siehe CLAUDE.md). Erstelle eine JavaScript-Funktion renderKarte(daten), die ein Array von Objekten auf einer Leaflet-Karte als Marker darstellt. Jedes Objekt hat die Felder: name (String), lat (Number), lng (Number), briefe (Number). Die Funktion soll die bestehende Karte (Variable map) verwenden und alte Marker entfernen, bevor neue gesetzt werden. Verwende L.circleMarker mit einer Groesse proportional zu briefe. Popup: Name und Briefanzahl.“
Beispiel: Schlechter Prompt ohne Kontext
„Mach mir eine Karte mit Markern.“
Agentic Coding: CE + RE + RV im Zusammenspiel
Context Engineering (CE): Gib dem LLM Projektstruktur, bestehenden Code und Datenbeispiele.
Requirement Engineering (RE): Beschreibe präzise, was die Komponente tun soll.
Code Review (RV): Prüfe den generierten Code systematisch auf Funktion, Konsistenz und Fehler.
Diese drei Kompetenzen bilden zusammen den Kern des Agentic-Coding-Workflows
für komplexe Projekte.
Ausprobieren
Erstellen Sie eine CLAUDE.md für Ihr eigenes DH-Projekt. Beschreiben Sie die Projektstruktur, die verwendeten Technologien und die wichtigsten Konventionen. Testen Sie dann, ob ein LLM mit dieser CLAUDE.md konsistenteren Code generiert.