- Astro 72.6%
- CSS 9.4%
- Python 7.3%
- TypeScript 6.7%
- Dockerfile 1.9%
- Other 2.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
## [1.2.1](https://git.trb.nrw/TRB/netzag-lousberg-web/compare/v1.2.0...v1.2.1) (2026-09-21) |
||
| .gitea/workflows | ||
| deployment | ||
| nginx | ||
| public | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| astro.config.mjs | ||
| CHANGELOG.md | ||
| check.py | ||
| Containerfile | ||
| LOGO-BRIEF.md | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| renovate.json | ||
| TODO.md | ||
| tsconfig.json | ||
netzag-lousberg.de
Website der NetzAG Lousberg (Verein in Gründung) — der studentischen Netzgruppe der Wohnheime Lousbergstraße 44 und 46 in Aachen.
Die Seite ist Pflichtvoraussetzung für die Akkreditierung als studentische Eigeninitiative an der RWTH Aachen (LUCOM-Formular, Feld „Social Media Auftritt“). Die Akkreditierungsstelle verlangt Vereinsdarstellung, Ziele, Bedingungen der Mitgliedschaft und Kontakt — alle vier sind vorhanden.
Bauen
npm ci # einmalig: Abhängigkeiten aus dem Lockfile
npm run dev # Entwicklungsserver mit Live-Reload
npm run build # erzeugt dist/
npm run check # astro check: Typen, Props, Templates
npm run verify # prüft das fertige dist/ (siehe unten)
Oder über den Makefile-Wrapper: make install, make dev, make build,
make check (führt astro check, den Build und check.py nacheinander aus),
make clean.
Gebraucht werden Node und npm; check.py braucht zusätzlich ein Python 3.
Das Container-Image bringt beides selbst mit und baut ohne beides auf dem
Rechner.
Technik
Astro 6, rein statisch (output: 'static', build.format: 'directory'),
ohne Integrationen und ohne Tailwind. Kein SSR, kein Client-JavaScript: Der
Build enthält kein einziges <script>-Tag.
Die Seite lief zunächst auf einem selbstgeschriebenen Generator aus
Python-Standardbibliothek. Das Argument dafür — möglichst wenige
Abhängigkeiten — trug nicht mehr, sobald klar war, dass der gemeinsame
TRB-CI-Workflow ohnehin eine package.json mit commitlint und
semantic-release verlangt: Der npm-Baum war längst da, nur für die Releases
statt für den Seitenbau. Dazu kam ein Ausfall, den ein etablierter Generator
als harten Fehler behandelt hätte (eine Inhaltsdatei ohne Frontmatter ergab
eine leere Seite, fehlerfrei gebaut). Den Ausschlag gab die Nachbarschaft:
trb-new-external fährt ebenfalls Astro, im selben Cluster über dieselbe CI.
Zwei Vereinsseiten mit zwei Build-Systemen sind für einen wechselnden
studentischen Vorstand schlechter als eine Technik, die er einmal lernt.
Gestaltung, Farbtokens, System-Font-Stack und prefers-color-scheme sind
eigen und wurden aus der Vorgängerversion unverändert übernommen — die
Nachbarseite sieht bewusst anders aus.
Verzeichnisse
astro.config.mjs statische Ausgabe, Verzeichnis-URLs, keine Integrationen
check.py Prüfskript für das fertige dist/ (siehe unten)
Makefile Wrapper um npm: install / dev / build / check / clean
Containerfile dreistufig: Astro-Build, Prüfung, nginx-Auslieferung
nginx/default.conf nginx-Konfiguration des Containers (Port 8080)
deployment/ Kustomize-Basis für Kubernetes (nicht von hier angewendet)
.gitea/workflows/ CI: baut und pusht das Image nach registry.trb.nrw
package.json Astro, commitlint und semantic-release
src/i18n/site.ts Seitenliste, Slugs je Sprache, Navigation, UI-Texte
src/layouts/ BaseLayout.astro — Grundgerüst, Kopf, Sprachlinks
src/components/ Header.astro, Footer.astro
src/styles/style.css das gesamte CSS, eine Datei
src/pages/*.astro Inhalte deutsch (vollständig)
src/pages/en/*.astro Inhalte englisch (Gerüst, wird bis zur Reakkreditierung
vervollständigt)
src/pages/sitemap.xml.ts sitemap.xml aus der Seitenliste, ohne Zusatzpaket
public/ wird unverändert übernommen: Favicon, robots.txt,
beitrittserklaerung.pdf
dist/ Ergebnis des Builds (nicht im Git)
Logo
Die Dateien liegen unter public/logo/ und sind damit unter
https://netzag-lousberg.de/logo/… abrufbar — für Briefkopf, Folien und
Dritte, die sich auf den Verein beziehen.
| Datei | wofür |
|---|---|
NetzAG_Lousberg_Logo_Light.svg |
Wort- und Bildmarke auf hellem Grund (#0b4f6c) |
NetzAG_Lousberg_Logo_Dark.svg |
dieselbe Form auf dunklem Grund (#7fc4e8) |
NetzAG_Lousberg_Logo_Monochrom.svg |
reines Schwarz, für Druck und Kopien |
NetzAG_Lousberg_Favicon_Quadrat.svg |
Bildmarke allein, quadratisch |
NetzAG_Lousberg_Favicon_16px.svg |
vereinfachte Fassung für 16 Pixel |
Markenrichtlinien_NetzAG.pdf |
Begründung der Form, Farbsystem, Mindestgrößen |
LIZENZ.md |
was mit dem Logo erlaubt ist und was nicht |
Die Bildmarke ist die Kontur des Lousbergs über zwei parallelen Linien: Die beiden Linien stehen für die zwei Häuser auf gleicher Höhe, ohne Über- und Unterordnung; der Bogen ist der Berg als Ortsanker.
Im Seitenkopf steckt die Wortmarke inline in src/components/Logo.astro,
mit currentColor statt fester Farbe — so trägt eine Form beide Farbmodi und
folgt dem Token --accent, das hell und dunkel genau die beiden
Originalfarben annimmt. Die Schrift ist in Pfade umgewandelt, es wird keine
Schriftdatei nachgeladen. Favicon: die quadratische Bildmarke, dazu die
16-Pixel-Fassung über sizes="16x16".
Die Wortmarke steht in Versalien (NETZAG LOUSBERG); im Fließtext bleibt es bei „NetzAG Lousberg“.
Was check.py prüft
astro check prüft Typen und Templates, sagt aber nichts über das Ergebnis.
Diese vier Prüfungen bringt kein Generator mit, deshalb hat das Skript die
Migration überlebt:
- Leere Seiten — Text zwischen
<main>und</main>, mit Mindestmaß. Genau der Fall, der einmal ein inhaltsleeres Impressum ausgeliefert hat. - Externe Ressourcen — jeder
href/srcmithttp- oderhttps-Schema ist ein Fehler. Das hält die Datenschutzerklärung einwilligungsfrei. - Tote Links — jeder interne Link und jeder PDF-Download muss auf eine Datei im Build zeigen.
- Platzhalter-Inventar — listet alle
[…]mit Fundstellenzahl.
Das Skript läuft als eigene Stufe im Containerfile: Fällt eine Prüfung
durch, entsteht kein Image.
Seiten
| Seite | deutsch | englisch |
|---|---|---|
| Start | / |
/en/ |
| Der Verein | /verein/ |
/en/about/ |
| Was wir machen | /was-wir-machen/ |
/en/what-we-do/ |
| Mitglied werden | /mitglied-werden/ |
/en/membership/ |
| Kontakt | /kontakt/ |
/en/contact/ |
| Impressum | /impressum/ |
/en/legal-notice/ |
| Datenschutz | /datenschutz/ |
/en/privacy/ |
Inhalte ändern
Jede Seite ist eine .astro-Datei: oben zwischen --- Titel und
Beschreibung, darunter der Inhalt als normales HTML im BaseLayout.
---
import BaseLayout from '../layouts/BaseLayout.astro';
const title = 'Kontakt';
const description = 'Kurzbeschreibung für Suchmaschinen.';
---
<BaseLayout lang="de" pageId="kontakt" title={title} description={description}>
<h1>Kontakt</h1>
…
</BaseLayout>
Kopf, Navigation, Sprachumschalter und Fuß kommen aus BaseLayout.astro und
src/i18n/site.ts und müssen nicht angefasst werden.
Neue Seite anlegen: einen Eintrag in src/i18n/site.ts unter pages
ergänzen (id, group, slugs, nav je Sprache), dann je Sprache eine
Datei unter src/pages/ bzw. src/pages/en/ mit genau dem Dateinamen aus
slugs. npm run check meldet Tippfehler in pageId, check.py findet
Links, die ins Leere zeigen.
Achtung bei geschweiften Klammern: In .astro-Dateien sind { und }
JavaScript-Ausdrücke — auch in Fließtext. Wer sie literal braucht, schreibt
{ bzw. }.
Platzhalter stehen durchgängig in eckigen Klammern und sind mit
<span class="todo">…</span> ausgezeichnet, damit sie im Browser auffallen.
make check listet sie auf; die vollständige Liste steht in
TODO.md.
Grundsätze
- Keine externen Ressourcen zur Laufzeit. Keine Web-Fonts von einem CDN,
keine Analytics, kein Drittanbieter-JS, keine eingebetteten Karten oder
Videos. Das ist der Grund, warum die Datenschutzerklärung kurz ist und kein
Einwilligungsbanner nötig wird.
make checkerzwingt es. - Kein JavaScript. Die Seite enthält keines — nicht als Askese, sondern weil sie keines braucht.
- Barrierefreiheit. Semantische Landmarks, Sprunglink,
lang- undhreflang-Auszeichnung, sichtbare Fokus-Rahmen, ausreichende Kontraste,prefers-color-schemefür hell und dunkel, responsives Layout ohne horizontales Scrollen. - Kein „e. V.“ Solange der Verein nicht eingetragen ist, führt er den Namen ohne Rechtsformzusatz; wo der Rechtsstatus zählt, steht „in Gründung“ bzw. „i. Gr.“.
Container
podman build -f Containerfile -t netzag-lousberg-web:dev .
podman run --rm -p 8080:8080 netzag-lousberg-web:dev
Der Build läuft im Image (Stufe 1 Python, Stufe 2 nginx-unprivileged); ein
vorher gebautes dist/ wird nicht benötigt. Der Container läuft
unprivilegiert als UID 101, hört auf Port 8080, Wurzelverzeichnis
/usr/share/nginx/html. Ein Health-Endpunkt liegt auf /healthz.
Deployment
Kubernetes, ausgerollt über ArgoCD. Dieses Repo liefert alles, was dafür nötig ist — angewendet wird von hier aus nichts:
| Teil | Wo |
|---|---|
Image-Build und Push nach registry.trb.nrw/trb/netzag-lousberg-web |
.gitea/workflows/build.yml, gemeinsamer Workflow TRB/ci-workflows |
| Versionierung und Bump des Image-Tags im Manifest | semantic-release, konfiguriert in package.json |
| Deployment, Service, Ingress (Apex) | deployment/ |
| ArgoCD-Application | nicht hier — argocd-config/applications/default/netzag-lousberg-web.yaml, von Hand anzulegen (path: deployment/, automated sync + selfHeal) |
Eckdaten für den Cluster: Ingress-Class nginx-public, TLS über cert-manager
mit dem ClusterIssuer letsencrypt-http01, Container unprivilegiert auf
Port 8080, Static-Root /usr/share/nginx/html, Health-Endpunkt
/healthz. Die Umleitung von www. auf den Apex macht der nginx im
Container, nicht der Ingress-Controller.
Zwei Dinge fehlen noch im Cluster und stehen in TODO.md: Der
ClusterIssuer letsencrypt-http01 muss angelegt werden (bisher existiert nur
letsencrypt-production mit einem DNS-01-Solver, der ausschließlich trb.nrw
abdeckt), und www.netzag-lousberg.de hat keinen A-Record — deshalb gibt es
vorerst kein www-Ingress.