Blog über meinen Fortschritt

Essay: Lernen, Verantwortung und die Arbeit mit KI

Über das Lernen mit KI, über Abkürzungen, Widerstand und die Verantwortung, den eigenen Denkprozess nicht zu überspringen.

· Essay • Reflexion • Lernen • KI

Ich lerne Programmieren in einer Zeit, in der sich eine neue Selbstverständlichkeit etabliert hat: Man kann fragen – und bekommt Antworten. Man kann beschreiben – und erhält Code. Man kann sich helfen lassen, geduldig, umfassend und jederzeit verfügbar.

Ich nutze diese Möglichkeit. Und ich habe gelernt, dass genau darin eine Verantwortung liegt.

Als ich begann, intensiver mit KI zu arbeiten, war das kein Ersatz für Lernen, sondern zunächst eine Vertiefung. Ich konnte nachfragen, Dinge umkreisen, Zusammenhänge aus verschiedenen Blickwinkeln betrachten. Besonders dann, wenn mich ein Thema wirklich interessierte, wurde der Dialog zu einem Denkraum, nicht zu einer Abkürzung.

Gleichzeitig wurde mir bewusst, dass diese Form des Lernens Fragen aufwirft – nicht nur für andere, sondern auch für mich selbst.

Die Abgabephase als Spiegel

Rückblickend waren die Tage rund um die Abgaben und Revisionen im Dezember besonders aufschlussreich. Nicht wegen spektakulärer Ergebnisse, sondern wegen der Reibung.

Ich habe offen kommuniziert, dass ich mir Unterstützung hole: für Erklärungen, für Perspektiven, für das Durchdenken von Konzepten. Ich habe auch eingeräumt, dass ich mir visuelle Elemente, Symbole oder sprachliche Formulierungen generieren lasse – dort, wo es nicht um das technische Kernverständnis, sondern um Darstellung geht.

Die Rückmeldungen darauf waren sachlich und unterstützend. Es wurde klar unterschieden zwischen Hilfe beim Denken und bloßem Kopieren, zwischen Inspiration und Ersatzleistung. Was mich besonders beruhigt hat: Es bestand kein Misstrauen. Aber es gab Leitplanken.

Klassisches Lernen und neue Werkzeuge

Mir wurde nahegelegt, technische Inhalte weiterhin klassisch zu vertiefen: Dokumentationen lesen, Referenzen nutzen, Dinge selbst ausprobieren. MDN, w3schools, echte Fehlermeldungen, echtes Ringen.

Ich verstehe diese Empfehlung – und ich weiß, dass sie richtig ist.

Gleichzeitig habe ich gemerkt, dass mein Lernweg nicht immer linear verläuft. Multiple-Choice-Tests oder reines Wiedergeben von Wissen fühlen sich für mich oft wie eine Simulation von Verständnis an, nicht wie Verständnis selbst.

Das heißt nicht, dass diese Formen wertlos sind. Aber sie greifen zu kurz, wenn es um Zusammenhänge, Verantwortung und Transfer geht.

Hier liegt für mich eine zentrale Spannung: Nicht zwischen alt und neu, sondern zwischen Wissen testen und Verstehen entwickeln.

Die eigentliche Gefahr

Die größte Gefahr in der Arbeit mit KI ist für mich nicht das Copy-Paste. Diese Form der Täuschung ist grob und leicht zu erkennen – auch für mich selbst.

Die subtilere Gefahr liegt woanders: im Überspringen des eigenen Ringens.

Wenn eine Erklärung zu glatt ist. Wenn eine Lösung sofort passt. Wenn kein Widerstand bleibt, an dem sich Verständnis aufbauen kann.

Nicht jede Antwort ist hilfreich, nur weil sie korrekt ist. Nicht jede Abkürzung spart Zeit – manche verlagern die Kosten nur nach hinten.

Diese Sensibilität habe ich mir nicht theoretisch erarbeitet, sondern praktisch: im Projekt, im Debugging, im Wiederlesen von eigenem Code, den ich zwar „verstanden“, aber noch nicht wirklich durchdrungen hatte.

KI als Verstärker

Was mir der Dialog mit KI dennoch ermöglicht hat, ist etwas anderes: einen Spiegel.

Fragen werden präziser, wenn man sie formulieren muss. Unklarheiten werden sichtbar, wenn man sie erklärt haben möchte. Und manchmal merkt man erst beim Lesen einer Antwort, was man eigentlich noch nicht verstanden hat.

In diesem Sinn ist KI für mich zwar sowohl Lehrer als auch Autor. Ich benutze sie jedoch eher als einen Verstärker dessen, was bereits da ist: Neugier, Unsicherheit, Strukturbedürfnis und Verantwortung.

Aber genau deshalb darf sie nicht allein stehen.

Ausprobieren als Grenze

Je länger ich mich mit Programmierung beschäftige, desto klarer wird mir: Verstehen beginnt dort, wo etwas nicht sofort funktioniert.

Erst im eigenen Ausprobieren entsteht Reibung – und damit Erkenntnis. Code ist kein Gedankengebäude, sondern ein System, das sich der Realität stellen muss: dem Browser, dem DOM, dem Timing, den Eigenheiten von Geräten und Nutzern.

Hier reicht es nicht, etwas verstanden zu haben. Es muss sich bewähren.

Diese Grenze ist wichtig. Und sie ist genau der Punkt, an dem KI bewusst zurücktreten muss.

Lernen findet nicht im Vakuum statt

Ich lerne nicht in einem geschützten Raum. Es gibt Tage mit körperlich fordernder Arbeit, Verpflichtungen, Erschöpfung. Diese Realität beeinflusst Konzentration, Geduld und die Art, wie man mit Widerstand umgeht.

In der Abgabephase wurde mir deutlich, wie wichtig es ist, dies zu bedenken – nicht als Ausrede, sondern als Teil von Verantwortung. Nachhaltiges Lernen braucht einen Rhythmus, der Belastung integriert, statt sie zu ignorieren.

Verantwortung statt Verbote

In Diskussionen über KI begegnen mir oft zwei Extreme: Euphorie oder Ablehnung. Beides greift zu kurz.

Was es braucht, ist keine Verbotskultur, sondern eine Haltung. Eine, die zwischen Unterstützung und Ersatz unterscheiden kann.

  • KI darf Denkprozesse begleiten, aber nicht ersetzen.
  • Sie darf erklären, aber nicht das eigene Ausprobieren überspringen.
  • Sie darf strukturieren, aber nicht die Auseinandersetzung abnehmen.

Diese Grenzen sind nicht immer scharf. Aber sie lassen sich spüren, wenn man aufmerksam bleibt.

Schluss

Dieser Text ist kein Standpunkt, sondern eine Momentaufnahme. Ein Versuch, eine Haltung zu beschreiben, während sie entsteht.

Ich lerne zu programmieren – und gleichzeitig lerne ich, wie ich lerne. Mit Unterstützung, ja. Aber nicht ohne Widerstand.

Wenn KI in diesem Prozess einen Platz hat, dann nicht als Abkürzung, sondern als Begleiter auf einem Weg, der eigenständig gegangen werden muss.

Nicht schneller. Aber bewusster.

8.–13. Dezember: Verdichtung, Pausen und struktureller Abschluss

Nicht jeder Fortschritt ist sichtbar. Diese Tage waren geprägt von Pausen, Belastung außerhalb des Projekts und einer abschließenden Phase der Verdichtung und Ordnung.

· Verdichtung • Pause • Struktur • Abschluss

1) Tage ohne Commit

Am 8. und 9. Dezember entstanden keine sichtbaren Änderungen im Repository. Diese Tage waren geprägt von äußeren Verpflichtungen und körperlich fordernder Arbeit außerhalb des Projekts.

Statt Fortschritt zu erzwingen, blieb die Entscheidung bewusst, Belastung zu akzeptieren und Pausen nicht als Rückschritt zu deuten.

2) Abstand als Teil des Prozesses

Auch wenn kein Code entstand, lief der Prozess weiter. Abstand erlaubt, Entscheidungen zu überprüfen, Überfrachtung zu vermeiden und die eigene Arbeitsweise zu regulieren.

Nicht jeder Tag muss produktiv wirken, um später tragfähig zu sein.

3) Rückkehr mit Ordnung und Lesbarkeit

Am 10. Dezember folgte eine bewusste Rückkehr ins Projekt mit Fokus auf Struktur und Klarheit: Lesbarkeit, Typografie und saubere Begrenzung für große Bildschirme.

Der Code wurde nicht erweitert, sondern reduziert und geordnet – Ballast entfernt, Entscheidungen gefestigt.

4) Abschluss ohne Abschlussdruck

Die letzten Tage bis zum 13. Dezember dienten der Klammer: kleinere Styling-Korrekturen, Header und Footer finalisieren, nichts Neues beginnen.

Kein Gefühl von „fertig“, aber ein Zustand, der trägt.

5) Fazit – Verdichtung statt Beschleunigung

Diese Phase markiert keinen Höhepunkt, sondern einen ruhigen Abschluss. Fortschritt zeigte sich nicht im Tempo, sondern in der Fähigkeit, Pausen zuzulassen und dennoch Struktur zu halten.

Genau darin liegt die Grundlage für die nächste Etappe.

4.–7. Dezember: Reibung & Umsetzung – Anforderungen werden Oberfläche

Der Wiedereinstieg wird praktisch: Debugging, UI-Feinschliff und klare Leitplanken. In diesen Tagen ging es weniger um neue Features, sondern um Tragfähigkeit: Responsiveness, Scroll-Gefühl, Lesbarkeit und saubere Mobile-Details.

· Projektarbeit • Reibung • UI/UX • Anforderungen

1) Vom Wiedereinstieg zur Reibung

Ab dem 4. Dezember wurde der Arbeitsmodus konkret. Nicht als Aufschwung, sondern als Reibung: Dinge wirkten noch nicht zuverlässig, Details verhielten sich unerwartet, und genau dadurch wurden sie bearbeitbar.

Der Fokus lag auf Ursache statt Symptom und auf Entscheidungen, die sich später nicht rächen.

2) Leitplanken der DA: nicht Dekoration, sondern Pflicht

In dieser Phase wurden Anforderungen sichtbar, die nicht „nice to have“ sind, sondern zur Abgabequalität gehören:

  • Responsiveness (UI muss auf Mobile/Tablet/Desktop funktionieren)
  • Smooth Scrolling (sauberes Scroll- und Bewegungsgefühl)
  • Wide-Screen-Tauglichkeit (große Displays dürfen nicht „auseinanderlaufen“)
  • Lesbarkeit (Größen, Abstände, klare Hierarchien)

Die Herausforderung lag weniger im Ob, sondern im Wie: Anforderungen so umzusetzen, dass die Oberfläche ruhig bleibt.

3) Konkrete Umsetzung im Repo (GitHub)

04.12.2025

  • bd58d53 – Responsive: Cart- und Button-Mechanik
  • 0679cb5 – Background-Hover für Artikel
  • 72326b7 – Pulse-Feintuning (Speed/Scale) für „pro“ Klick-Feedback
  • c3ef156 – Bugfix: letztes Item verdeckt → Padding in templates.js
  • 1aee125 – Footer
  • aa38f19 – Reaktion auf Bezahlen (payForOrder)
  • 9b897f9 – SVG CloseCart (responsive)
  • c88e0e0 – Hover für CloseCart-Button

05.12.2025

  • 8edd7fc – Smooth Scroll Behaviour, Search-Button vorerst deaktiviert (JS folgt später), CloseCart nur Mobile

06.12.2025

  • 24da330 – Smooth Scrolling + Cart-Layout (left) + Background Blur

07.12.2025

  • 474140a – Cart-Finetuning
  • db8d138 – Cart-Fonts + Clamp-Feinschliff
  • 6314cc1 – Console-Logs reduziert (nur erklärende Marker bleiben)

4) Fortschritt zeigt sich im Widerstand

Diese Tage waren nicht „Feature-getrieben“, sondern qualitätsgetrieben. Viel Arbeit zeigt sich hier nicht in großen neuen Bausteinen, sondern in Korrekturen, die man erst beim echten Benutzen bemerkt: Abstände, Click-Feedback, Mobile-Details, Lesbarkeit.

Reibung ist nicht das Gegenteil von Fortschritt – sie ist oft sein Beweis.

5) Fazit – Tragfähigkeit entsteht durch Details

Am Ende dieser Phase war nicht „alles fertig“, aber vieles verlässlicher: responsive Verhalten, klarere UI-Rückmeldungen, ruhigeres Scroll-Gefühl und ein aufgeräumteres Debugging.

Nicht Geschwindigkeit war das Ziel, sondern Substanz.

1.–3. Dezember · Wiedereinstieg

Kein Neubeginn, sondern ein Wiederanknüpfen. In diesen Tagen kehrte weniger der Output zurück, sondern die Fähigkeit, das Projekt wieder ruhig und tragfähig anzufassen.

· Wiedereinstieg • Projektarbeit • Fokus

1) Wiederanknüpfen statt Neustart

Der Wiedereinstieg Anfang Dezember war kein Neubeginn, sondern ein Wiederanknüpfen. Die Motivation war vorhanden – was zurückkehren musste, war die Fähigkeit, komplexe Dinge wieder anzufassen, ohne sie zu beschleunigen oder zu vereinfachen.

2) Projekt öffnen, Modell prüfen

In diesen Tagen stand kein sichtbarer Fortschritt im Vordergrund. Ich habe das Projekt geöffnet, Code gelesen, Strukturen geprüft und Zusammenhänge reaktiviert.

Nicht mit dem Ziel, schnell voranzukommen, sondern um sicherzugehen, dass das innere Modell wieder trägt.

3) Fragen werden wieder technisch

Ein deutliches Zeichen für diesen Übergang: Die Fragen wurden wieder technisch. Nicht mehr ob etwas Sinn ergibt, sondern warum sich ein DOM-Element so verhält, wo Zuständigkeiten liegen und was Ursache statt Symptom ist.

4) Richtung statt Tempo

Wichtig war mir in dieser Phase, nichts aufzuholen und nichts zu beweisen. Der Fokus lag auf Richtung, nicht auf Tempo – auf tragfähigen Schritten statt auf sichtbarem Output.

5) Fazit – Übergang vollzogen

Mit dem 3. Dezember war dieser Übergang vollzogen. Der Faden war nicht nur gehalten, sondern wieder fest in der Hand.

Ab hier konnte Arbeit entstehen, die Reibung zulässt und deshalb trägt.

Zwischen Stillstand und Wachstum – warum Entwicklung auch Leerlauf braucht

Nicht jede Phase des Lernens ist sichtbar produktiv. Manchmal sitzt man vor dem Bildschirm, bewegt nichts – und arbeitet dennoch. Dieser Text ist ein Versuch, genau diesen Zwischenraum ernst zu nehmen.

· Lernen • Haltung • Entwicklung

1) Wenn Entwicklung nach außen still aussieht

Es gibt diese Momente, die jeder Entwickler kennt: Man sitzt vor dem Bildschirm, tippt nichts, klickt nichts, produziert nichts Sichtbares.

Von außen wirkt das wie Stillstand. Von innen jedoch laufen Prozesse, die sich kaum beschleunigen lassen: Abwägen, Verwerfen, Neuordnen.

Lösungen entstehen nicht immer durch Aktion, sondern oft durch Verdichtung. Wer nur sichtbaren Output als Arbeit zählt, verkennt einen großen Teil dessen, was Entwicklung überhaupt möglich macht.

2) Dranbleiben heißt nicht immer „weitermachen“

In den letzten Wochen gab es Phasen, in denen mein Fortschritt gering war. Wenig Code, wenig neue Features, wenig, das man vorzeigen könnte.

Und doch war genau hier eine Entscheidung notwendig: dranbleiben – nicht heroisch, nicht euphorisch, sondern nüchtern.

Dranbleiben kann heißen:

  • das Projekt nicht zu schließen
  • den Anspruch nicht zu verleugnen
  • das eigene Tief nicht zum Abbruchgrund zu machen

Diese Form von Kontinuität ist leise, aber sie ist tragfähig.

3) Lernen ist stärker als der Schatten

Jede längere Lernphase bringt Schatten mit sich: Zweifel, Trägheit, Selbstkritik, manchmal auch Flucht in Ablenkung.

Entscheidend ist nicht, ob diese Phasen auftreten – sondern, wie man mit ihnen umgeht.

Mein Weg in dieser Zeit war kein makelloser. Aber mein Wille zu lernen war stärker als die Versuchung, das Ganze innerlich abzuwerten.

Für mich ist das keine Schwäche, sondern eine Kernkompetenz: Lernen trotz Ambivalenz.

4) Nachhaltiges Wachstum braucht menschliche Maßstäbe

Technische Systeme werden oft auf Effizienz optimiert. Menschen funktionieren anders.

Nachhaltiges Wachstum – auch wirtschaftlich – berücksichtigt, dass Lernfähigkeit, Kreativität und Belastbarkeit nicht linear skalieren.

Phasen reduzierter Produktivität sind kein Defizit, sondern Teil eines gesunden Systems.

Wer sie ignoriert, zahlt später den Preis – mit Ausfällen, Fluktuation oder stagnierender Innovation.

5) Das träge Unterbewusstsein als Arbeitsraum

Ein Teil des Lernens geschieht nicht im bewussten Problemlösen, sondern im Hintergrund.

Dieses „Datamining“ im Unterbewusstsein birgt Risiken: Unruhe, Zerstreuung, das Gefühl, nichts zu kontrollieren.

Gleichzeitig schafft es etwas Wertvolles: einen mentalen Freiraum, in dem Ideen nicht erzwungen, sondern gefunden werden.

Kreativität entsteht selten im Dauerlauf. Sie braucht ein stabiles Fundament – einen freien, belastbaren inneren Raum.

6) Fazit – Entwicklung ist mehr als Output

Diese Phase war kein Glanzstück in Zahlen oder Ergebnissen. Aber sie war Teil meines Weges als Entwickler.

Nicht trotz ihrer Langsamkeit, sondern gerade wegen ihr.

Lernen heißt nicht, immer sichtbar voranzukommen. Lernen heißt, auch dann nicht auszusteigen, wenn Fortschritt leise wird.

27. November: Gestaltung denken – wenn UI Entscheidungen erzwingt

Heute ging es weiterhin um Entscheidungen: Wie fühlt sich eine App an? Wie bewegt sich ein Nutzer? Und wie zwingt gutes UI zu klarer Struktur im Code?

· UI/UX • Interaktion • Systemdenken

1) Vom Code zum Nutzer

Heute war die Frage, wie sich die App für jemanden anfühlt, der sie benutzt.

Das ist ein anderer Denkmodus. Einer, der Code nicht isoliert betrachtet, sondern als Mittel, um Entscheidungen sichtbar zu machen.

2) Toggle-Ideen und responsive Gedanken

Ein zentrales Thema war die Frage, wie Nutzer auf kleineren Screens zwischen Menü und Warenkorb wechseln sollen.

Ein Toggle-Button – vielleicht ein X im Kreis, vielleicht eine klare visuelle Geste – schien plötzlich nicht nur sinnvoll, sondern notwendig.

Solche Entscheidungen sind nie rein visuell. Sie erzwingen Struktur im Code, Zuständigkeiten und klare Zustandswechsel.

3) UI zwingt zur Ehrlichkeit

Heute wurde mir wieder bewusst, dass UI keine Unschärfen verzeiht. Ein Nutzer merkt sofort, wenn etwas unklar ist – auch wenn der Code technisch korrekt ist.

„Gutes UI entlarvt schlechte Logik.“

Diese Erkenntnis hat meine Sicht auf Frontend-Arbeit nachhaltig geprägt.

4) Systemdenken in Interaktionen

Jede Interaktion ist ein Zustand. Jeder Zustand braucht Regeln.

Heute habe ich begonnen, Interaktionen nicht mehr als einzelne Events zu sehen, sondern als Teil eines Zustandsmodells:

  • Menü sichtbar
  • Cart sichtbar
  • Beide sichtbar
  • Übergangszustände

Diese Denkweise verändert, wie man JavaScript schreibt.

5) Fazit – Gestaltung ist Zwang zur Klarheit

Der 27. November war ein kreativer Tag – aber kein chaotischer. Im Gegenteil: Er hat Klarheit erzwungen.

Gute Gestaltung zwingt zu Entscheidungen. Und gute Entscheidungen führen fast immer zu besserem Code.

26. November: Sprache schärfen – wenn präzises Denken im Code sichtbar wird

Heute ging es um Begriffe, Benennungen und innere Modelle. Nicht viel Neues gelernt – aber vieles klarer formuliert. Ein Tag, an dem Denken und Code wieder enger zusammengerückt sind.

· JavaScript • Präzision • Strukturdenken

1) Wenn Begriffe plötzlich wichtig werden

Heute habe ich gemerkt, dass sich mein Fokus verschoben hat: Weg vom bloßen „Lösen“ von Problemen, hin zum präzisen Benennen dessen, was eigentlich passiert.

Variablen, Funktionen, Zuständigkeiten – alles begann, wieder stärker als Sprache zu wirken. Und Sprache entscheidet, wie gut Denken transportiert werden kann.

2) Der Unterschied zwischen „funktioniert“ und „ist verstanden“

Ein wiederkehrendes Gefühl heute: Code kann funktionieren, ohne wirklich verstanden zu sein.

Ich habe mir bewusst Zeit genommen, Stellen zu hinterfragen, die ich zuvor einfach akzeptiert hätte:

  • Warum heißt diese Variable so?
  • Welche Verantwortung trägt diese Funktion wirklich?
  • Ist das hier Logik – oder nur ein Workaround?

Diese Fragen sind unbequem, aber sie markieren den Übergang vom Anwenden zum Beherrschen.

3) Mentales Refactoring

Interessanterweise habe ich heute weniger Code refactored, dafür aber viel im Kopf umgebaut.

„Wenn du den Code nicht erklären kannst, hast du ihn noch nicht wirklich geschrieben.“

Dieser Gedanke hat mich den ganzen Tag begleitet. Verstehen heißt erklären können – notfalls mir selbst.

4) Der eigene Lernmodus wird sichtbar

Heute wurde mir bewusst, dass ich mich in eine neue Phase bewege: Ich lerne nicht mehr primär durch neue Themen, sondern durch Vertiefung.

Das fühlt sich weniger spektakulär an, ist aber deutlich nachhaltiger. Es ist die Phase, in der aus Wissen Kompetenz wird.

5) Fazit – Klarheit ist Fortschritt

Der 26. November war ruhig, konzentriert und unspektakulär – und genau deshalb wertvoll.

Kein neues Feature, kein großer Aha-Moment. Aber mehr Klarheit, bessere Sprache, stabileres Denken.

Und das ist oft der entscheidende Schritt nach vorn.

25. November: Wieder im Flow – Struktur denken, nicht nur reparieren

Heute war wieder ein echter Arbeitstag: Code lesen, Logik hinterfragen, Zusammenhänge erkennen. Nicht hektisch, aber klar fokussiert – der Flow war zurück.

· JavaScript • Struktur • Lernprozess

1) Der Unterschied zwischen „wieder da“ und „wirklich da“

Nach den stabilisierenden Tagen zuvor hat sich heute etwas verändert: Ich war nicht mehr nur präsent, sondern wieder aktiv denkend.

Der Code fühlte sich nicht fremd an. Im Gegenteil – ich konnte ihn lesen, hinterfragen und an mehreren Stellen gleichzeitig denken, ohne innerlich aus dem Tritt zu geraten.

2) Vom Fixen zum Verstehen

Heute ging es weniger darum, konkrete Fehler zu „fixen“, sondern darum, Muster zu erkennen:

  • Warum ist diese Funktion hier?
  • Welche Annahmen stecken hinter dieser Logik?
  • Was würde passieren, wenn sich die Daten ändern?

Das ist ein anderes Arbeiten als am Anfang meiner Lernreise. Weniger reaktiv, mehr strukturell.

3) JavaScript als Sprache, nicht als Werkzeugkiste

Ein wichtiger Gedanke des Tages: Ich benutze JavaScript immer weniger als Sammlung einzelner Tricks und immer mehr als zusammenhängende Sprache.

Bedingungen, Rückgabewerte, Funktionen und Datenmodelle beginnen, sich wie Teile eines Ganzen zu verhalten – nicht wie isolierte Lösungen.

„Wenn man aufhört, nach Syntax zu suchen, fängt man an, Logik zu sehen.“

4) Konzentration ohne Druck

Auffällig war heute auch die Art der Konzentration: ruhig, ohne Stress, ohne das Gefühl, etwas erzwingen zu müssen.

Ich habe gemerkt, dass mein Umgang mit Unsicherheit sich verändert: Nicht mehr sofortiger Widerstand, sondern Neugier.

5) Fazit – Ein still produktiver Tag

Der 25. November war kein spektakulärer Tag mit großen Features. Aber er war produktiv auf die richtige Weise: Verständnis vertiefen, Zusammenhänge sehen, Vertrauen ins eigene Denken zurückgewinnen.

Genau diese Tage bauen langfristige Kompetenz auf.

24. November: Zurück in die Struktur – Vertrauen ins System wiederfinden

Nach dem Kontrollverlust des Vortags ging es heute nicht um Tempo, sondern um Stabilität: Ordnung herstellen, Vertrauen zurückgewinnen und den Fokus behutsam wieder auf Code lenken.

· Stabilisierung • Struktur • Bestell-App

1) Der erste Schritt zurück: Überblick statt Aktion

Nach der Kernel Panic vom Vortag war klar: Heute ist kein Tag für Experimentierfreude. Stattdessen ging es darum, wieder Vertrauen in mein Setup und meine Werkzeuge zu bekommen.

Ich habe mir bewusst Zeit genommen, das Projekt zu öffnen, ohne sofort etwas ändern zu wollen. Erst schauen. Erst lesen. Erst wieder ankommen.

2) Struktur wirkt beruhigend

Interessanterweise stellte sich die Ruhe nicht durch „Lösen“ ein, sondern durch Struktur.

  • Dateien überfliegen
  • Ordnerlogik nachvollziehen
  • Benennungen prüfen
  • Zusammenhänge wiedersehen

Das System war nicht kaputt. Es hatte mich nur kurz daran erinnert, wie tief es eigentlich ist.

3) Zurück zur Bestell-App – mit verändertem Blick

Erst gegen später habe ich mich wieder aktiv mit der Bestell-App beschäftigt. Nicht, um Features zu bauen, sondern um zu prüfen, ob meine mentale Landkarte noch stimmt.

Und sie stimmte. Die Logik war da. Die Struktur war da. Der Fokus ließ sich wieder herstellen.

4) Ein leiser Perspektivwechsel

Der gestrige Tag hatte etwas verschoben: Ich habe heute bewusster wahrgenommen, dass Stabilität kein Selbstläufer ist.

„Produktivität beginnt dort, wo Vertrauen in die Werkzeuge besteht.“

Das gilt für Betriebssysteme genauso wie für den eigenen Kopf.

5) Fazit – Wieder Boden unter den Füßen

Der 24. war kein lauter Tag. Aber er war notwendig, um den Boden wieder zu spüren.

Morgen darf es wieder konkreter werden. Heute ging es darum, Stabilität nicht zu erzwingen, sondern sie langsam zurückkehren zu lassen.

23. November: Kernel Panic – wenn das System unter den Füßen verschwindet

Ein Screenshot, ein schwarzer Bildschirm, kryptische Meldungen. Heute ging es nicht um JavaScript oder UI, sondern um das Fundament: das Betriebssystem selbst – und das Gefühl, plötzlich ganz unten zu stehen.

· Linux • Systemverständnis • Kontrolle

1) Der Moment, in dem nichts mehr geht

Heute hatte ich zum ersten Mal bewusst mit einer Kernel Panic zu tun. Kein sanfter Fehler, kein Stacktrace, kein Hinweis darauf, wo ich anfangen könnte zu suchen.

Stattdessen: ein eingefrorener Bildschirm, kryptische Zeilen, und das Gefühl, dass das System unter mir einfach weggezogen wurde.

2) Wissen aus der Tiefe des Gedächtnisses

Irgendwo ganz hinten im Kopf tauchte ein Begriff auf: REISUB.

Eine Tastenkombination aus alten Linux-Zeiten, gedacht für genau solche Situationen – um ein System kontrolliert neu zu starten, selbst wenn es nicht mehr reagiert.

In diesem Moment wurde mir klar, wie viel Wissen nicht aktiv, sondern latent vorhanden ist. Es schläft, bis es gebraucht wird.

3) Die Grenze zwischen Nutzer und System

Eine Kernel Panic ist etwas völlig anderes als ein Programmfehler. Hier gibt es keine Fehlermeldung, die man „versteht“, kein freundliches Debugging.

Man steht an der Grenze:

„Ab hier bist du nicht mehr Anwender – ab hier beginnt das System selbst.“

Dieser Gedanke hat mich mehr beschäftigt als die Ursache des Problems.

4) Demut vor der Tiefe der Technik

Heute wurde mir wieder bewusst, wie viele Schichten unter dem liegen, womit ich mich normalerweise beschäftige.

  • Frontend
  • JavaScript-Engine
  • Betriebssystem
  • Kernel

Und jede dieser Schichten folgt eigenen Regeln. Man kann sehr kompetent sein – und trotzdem plötzlich völlig handlungsunfähig wirken.

5) Fazit – Kontrollverlust als Lernmoment

Der heutige Tag war unangenehm, aber lehrreich. Nicht, weil ich das Problem gelöst habe, sondern weil ich gesehen habe, wo meine Grenzen aktuell liegen.

Programmieren bedeutet nicht nur, Kontrolle zu gewinnen, sondern auch zu akzeptieren, dass es Ebenen gibt, die man (noch) nicht beherrscht.

Und genau dort beginnt oft das nächste Lernfeld.

22. November: Ein Tag der Langsamkeit – Körper, Chemie und das stille Wissen über Balance

Heute stand das Coden im Hintergrund. Stattdessen ging es um Gesundheit, um Ursachen und Wirkungen im Körper – und um die Erkenntnis, dass Regeneration genauso viel Teil des Weges ist wie Lernen.

· Gesundheit • Lernen • Balance

1) Wenn das Leben den Fokus neu setzt

Der Tag begann nicht mit JavaScript, sondern mit einer medizinischen Frage: Was ist Fluconazol? Eine kleine, unscheinbare Tablette – aber mit einer Wirkung, die direkt in die Biochemie eingreift.

Lua und ich hatten beide Symptome, und plötzlich war der Fokus nicht mehr Variablen-Benennung, sondern: „Was passiert eigentlich in meinem Körper?“

2) Wenn man eine Krankheit versteht, verliert sie ihren Schrecken

Ich habe mich mit dem Wirkmechanismus beschäftigt: Wie Fluconazol Pilze nicht „abtötet“, sondern ihnen einen zentralen Baustein der Zellmembran entzieht. Ein wissenschaftlicher Blick – aber auch ein beruhigender.

Man versteht: Es ist ein geregelter Prozess, kein dunkles Unbekanntes.

3) Slow Day – und warum er trotzdem wertvoll war

Technisch ist heute nichts passiert. Kein Code, kein neues Feature, keine komplexe Erkenntnis.

Und dennoch war es ein Tag des Lernens:

  • Wie wichtig Ruhe ist, bevor Überlastung entsteht.
  • Wie eng körperliche Gesundheit und geistige Klarheit verbunden sind.
  • Wie schnell der Kopf mitfiebert, wenn der Körper aus dem Gleichgewicht ist.

Der Tag hatte eine Einfachheit, die selten geworden ist.

4) Ein leiser Gedanke über Balance

Während ich mich mit den Informationen zu Fluconazol beschäftigt habe, kam dieser Gedanke hoch:

„Programmieren ist wie Körperchemie: Wenn ein kleines Teil aus der Balance gerät, verändert sich das ganze System.“

Es war ein unerwarteter Gedanke – aber er passt. Auch beim Coden sind es nicht die großen Fehler, die Systeme instabil machen, sondern die kleinen Dinge, die übersehen werden.

5) Fazit – Ein unerwarteter, aber notwendiger Tag

Heute war kein Lernfortschritts-Tag und auch kein produktiver Tag im klassischen Sinne. Aber er war notwendig.

Meine Energie wird zurückkommen. Die nächsten Tage gehen weiter – aber heute durfte der Körper entscheiden.

21. November: Ternary-Verständnis, Funktionslogik und ein Schritt zu mehr Klarheit

Heute habe ich mich tief in den Ternary-Operator, Wahrheitswerte, Bedingungslogik und den Fluss von Funktionen hineingedacht – und gemerkt, wie viel auf den ersten Blick einfache Syntax wirklich transportiert.

· JavaScript • Logik • Verständnis

1) Der Tag beginnt mit einer Frage: „Was prüft der Ternary eigentlich wirklich?“

Es begann mit einer scheinbar einfachen Frage: Prüft der Ternary wirklich nur true/false, oder steckt mehr dahinter?

Und genau hier wurde es spannend. Ich habe verstanden:

Der Ternary prüft keine Werte – er prüft Wahrheitsgehalte.

Alles ist „truthy“ oder „falsy“: undefined, null, 0, leere Strings, NaN → alle führen in den false-Zweig.

Diese Erkenntnis hat mir das Denken in Bedingungen dauerhaft erleichtert.

2) Der Moment, in dem Logik plötzlich „klackt“

Ein wichtiger Aha-Moment heute: Zu verstehen, dass der Ternary nicht nur ein Kürzel für if/else ist, sondern ein Ausdruck, der zurückgibt – er produziert Werte.

Das heißt:

  • Ternary kann Variablen zuweisen
  • Ternary kann Funktionsaufrufe auslösen
  • Ternary kann sogar Templates oder komplexe Ausdrücke bauen

Das hat die Art geändert, wie ich Logik strukturiere.

3) Die tiefere Einsicht: Warum manche Dinge klappen – und andere nicht

Was heute besonders hängen blieb, war der Zusammenhang zwischen Existenz und Intention.

Ein Wert kann existieren, aber trotzdem „falsy“ sein. Ein Wert kann nicht existieren und dennoch in Bedingungen indirekt weitergereicht werden.

Dieser kleine Denkfehler war der Grund für mehrere Stunden Unsicherheit.

„Er prüft nicht, ob die Variable existiert – er prüft, wie sie sich verhält.“

4) Mehr Sicherheit in Funktionsfluss und Fehlersuche

Heute habe ich außerdem begriffen, warum mein Gehirn manchmal auf „Syntaxfehler“ reagiert, obwohl der Code technisch korrekt ist: Manchmal fehlt nur ein mentales Modell, nicht eine Zeile Code.

Und plötzlich wurde klar: Der Ternary ist ein Werkzeug – aber nur dann, wenn die Logik dahinter verstanden ist.

5) Fazit – Ein Tag, der Denkkraft statt Codezeilen erzeugt hat

Heute ist nicht viel Sichtbares entstanden – aber unglaublich viel Unsichtbares: Verständnis, Klarheit, Struktur.

Diese Art von Tag ist selten, aber wichtig: Nicht produktiv im klassischen Sinn, sondern produktiv im Kopf.

Morgen geht es wieder mehr ins Konkrete – aber die Tiefe von heute wird dort den Unterschied machen.

20. November: Strukturdenken, Fehlerkultur und das unsichtbare Fundament einer App

Heute stand weniger das Schreiben von Code im Mittelpunkt als das Verstehen von Systemen: Datenmodelle, Fehlerursachen und die Frage, warum manche Bugs erst durch ein bestimmtes Bewusstsein sichtbar werden.

· Systemdenken • Debugging • JavaScript

1) Der Tag beginnt mit einer Frage: Warum tun Systeme, was sie tun?

Heute habe ich mich nicht hingesetzt, um „ein Feature zu bauen“, sondern um etwas sehr Grundsätzliches zu verstehen: Wie verhalten sich Systeme, wenn man ihnen nicht genug Struktur gibt?

Dabei fiel mir auf, dass ich gerade in den letzten Tagen viel über kleine Fehler gesprochen habe – aber heute ging es darum, die Mechanik hinter den Fehlern zu begreifen.

2) Der unscheinbare Fehler als Spiegel

Ein wiederkehrendes Thema: Ein kleiner Logikfehler oder ein minimaler Benennungsunterschied reicht aus, um das ganze System aus dem Tritt zu bringen.

Es ist, als ob das System mir etwas sagen will:

„Ich bin konsistent, solange du konsistent bist.“

Dieser Gedanke hat heute mehr mit mir gemacht als jede Zeile Code.

3) Was bedeutet „Verstehen“ beim Coden?

Verstehen heißt nicht, dass ich weiß, warum Code funktioniert. Verstehen heißt, die Prinzipien zu erkennen, die unabhängig vom konkreten Projekt gelten:

  • Struktur schlägt Intuition.
  • Konsistenz schlägt Komplexität.
  • Einheitliche Modelle schlagen Ad-hoc-Lösungen.

Das ist eine Art von Wissen, die sich langsam prägt – und nur an solchen Tagen überhaupt die Chance hat zu entstehen.

4) Zwischen Technik und Philosophie

Während ich über Datenmodelle und Fehlerursachen nachgedacht habe, kam ein interessanter Gedanke hoch:

„Systeme sind wie Menschen: Sie reagieren nicht auf das, was du willst, sondern auf das, was du tatsächlich tust.“

Ein Name, der nicht passt. Ein Index, der nicht existiert. Ein Wert, der nicht geprüft wird.

Alles kleine Beispiele für dieselbe Wahrheit: Ein System kennt keine Absicht – nur Ausführung.

5) Fazit – Ein Tag der Klärung

Ich habe heute nicht viel „gebaut“, aber sehr viel verstanden. Der Tag war wie ein Gespräch mit dem System selbst: Wo bist du klar? Wo bin ich unklar? Wo müssen wir uns gegenseitig besser lesen?

Morgen geht es wieder stärker ins konkrete Coden – aber heute war ein wichtiger Schritt zurück zu strukturiertem Denken.

19. November: Der Fokus ist zurück – kleine Fehler, große Einsichten

Der Tag, an dem das Denken wieder scharf wurde: Variablen, Datenmodelle, String-Konsistenz – und die Erkenntnis, dass kleine Inkonsistenzen oft der Schlüssel zu großen Aha-Momenten sind.

· JavaScript • Datenmodelle • Erkenntnisse

1) Ein kleiner Trigger – und plötzlich ist der Fokus zurück

Heute passierte etwas Typisches: Ein winziger Fehler im Code löste eine überproportional große Kaskade an Verstehen aus. Genau so funktionieren viele meiner Lernsprünge.

Die Ursache war ein scheinbar belangloser String, der nicht exakt dem erwarteten Muster entsprach. Doch aus diesem Mini-Problem entstand ein ganzer Gedankengang über Konsistenz, Naming-Conventions und die Fragilität von gereihten Bedingungen.

2) Variablen-Benennung – mehr als nur Ästhetik

Ich habe mich heute tiefer mit der Frage beschäftigt, warum konsistente und präzise Variablen-Namen nicht nur „nett“, sondern fundamental sind.

  • Klarheit → weniger mentale Last
  • Konsistenz → weniger Fehlerquellen
  • Lesbarkeit → Zukunftssicherheit

Ein vermeintlich lächerlicher Unterschied von 3–4 Zeichen kann der Unterschied sein zwischen einem sauberen Flow und einer Stunde Debugging.

3) Die philosophische Dimension des Debuggens

Während ich den Fehler betrachtete, kam ein Gedanke auf: Systeme zeigen dir, wie sie funktionieren wollen. Die Aufgabe beim Programmieren ist nicht, das System zu zwingen, sondern zu verstehen, wie es logisch und harmonisch zusammenwirkt.

Bevor du beweist, dass ein Flugzeug fliegt, akzeptiere, dass es fliegt.

Dieser Satz, der heute unbewusst auftauchte, beschreibt meine Beziehung zur Software ganz gut: Erst akzeptieren, dann analysieren.

4) Vom Fehler zur Einsicht – das Muster

Der Tag folgte einem vertrauten inneren Ablauf:

  1. Ein kleiner Fehler macht etwas Großes sichtbar.
  2. Der Geist wehrt sich kurz – dann öffnet er sich.
  3. Die Struktur dahinter wird klar.
  4. Und ein Stück tieferes Verständnis stabilisiert sich.

Heute war es das zentrale Thema der Konsistenz.

5) Fazit – Der 19. war kein Coding-Tag, sondern ein Einsichts-Tag

Ich habe heute nicht viele Zeilen Code geschrieben. Aber ich habe ein wichtiges Prinzip tiefer verstanden: Dass Systeme nicht durch Größe wachsen, sondern durch Klarheit.

Der Fokus ist eindeutig zurück. Und ab morgen geht es wieder wirklich ans Entwickeln.

16.–18. November: Die Rückkehr zur Klarheit – Konzepte schärfen, Fehler verstehen, Fokus zurückgewinnen

Drei Tage zwischen Aufwachen und Wiederfinden. Noch nicht voller Geschwindigkeit, aber klar im Aufwärtstrend: Strukturen wurden sortiert, JavaScript-Konzepte verinnerlicht, und die Bestell-App rückte erneut ins Zentrum des Lernens.

· JavaScript • Bestell-App • Lernprozess

1) 16. November – Erste Aktivierung nach der Pause

Der 16. war einer dieser Tage, an denen der Motor wieder anspringt, aber noch nicht rund läuft. Ich habe mich erneut in die Struktur der Bestell-App eingelesen und versucht, die vorhandenen Datenmodelle und UI-Elemente wieder zu verknüpfen.

Es war ein Tag des „inneren Sortierens“: Wo bin ich im Modul? Was brauche ich als Nächstes? Welche offenen Fragen schlummern im Code? Noch wenig Output, dafür viel Neuorientierung – ein notwendiger Schritt, um wieder Fokus aufzubauen.

2) 17. November – Die Konzepte beginnen wieder zu greifen

Der 17. brachte deutlich mehr Energie. Ich habe mich intensiver mit wiederkehrenden JavaScript-Konzepten beschäftigt: Datenbereinigung, String-Vergleiche, IDs, und die wichtige Frage, wie man Strukturen so baut, dass sie nicht bei der kleinsten Inkonsistenz kollabieren.

Aus dieser Phase stammt auch eine Einsicht, die sich durch den ganzen Monat ziehen wird:

Ein System ist so stark wie seine kleinste, vermeintlich triviale Verbindung.

Ich begann außerdem, wieder bewusster über das Zusammenspiel von Logik und Darstellung nachzudenken. Die App wurde langsam wieder zum Projekt, nicht nur zur Übungsaufgabe.

3) 18. November – Das Denken wird wieder scharf

Am 18. war der Fokus endgültig zurück. Ich merkte es daran, dass meine Fragen präziser wurden: Warum funktioniert ein Ausdruck? Was bedeutet Konsistenz im Datenmodell wirklich? Welche Fehler führen zu welchen Symptomen?

Außerdem nahm das strukturelle Denken wieder Fahrt auf: Wie baut man Templates? Wie sichert man UI-Elemente gegen Fehler ab? Und wie sorgt man dafür, dass man den eigenen Code später versteht – nicht nur jetzt?

Dieser Tag markierte den Übergang vom passiven „Wiederfinden“ zum aktiven Entwickeln. Der Fokus war wieder da.

4) Fazit – Vom Leerlauf zurück ins Fließen

Diese drei Tage waren wie das Anfahren eines schweren Fahrzeugs: Zuerst ruckelt es, dann setzt es sich langsam in Bewegung, und schließlich läuft es wieder stabil.

Ich habe den Wiedereinstieg gefunden – nicht durch große Features, sondern durch das Schärfen der Grundlagen. Ab hier wird das Lernen wieder konkret, und ab Morgen beginnt eine neuer Weg.

13.–15. November: Die stillen Tage – Struktur finden, Fehler verstehen, Oberfläche formen

Drei unscheinbare Tage zwischen Pause und Neubeginn. Datenmodelle, UI-Feinschliff und das Gefühl, dass sich im Hintergrund wieder etwas ordnet – auch wenn sich die Schritte klein anfühlten.

· Bestell-App • Lernprozess

1) 13. November – Rückkehr ins Modul 7

Nach einer Phase mit wenig Fokus auf die Bestell-App fühlte sich dieser Tag wie ein vorsichtiges Wiedereintauchen an. Das Projekt war nicht fremd, aber es hat mich getestet: Ein kleiner, beinahe lächerlich wirkender Fehler im Datenmodell zeigte mir, wie wichtig exakte Konsistenz ist.

category.id und menu.category müssen exakt übereinstimmen – logisch, und doch der Auslöser für mein wichtigstes Aha dieses Tages. Solche Fehler sind wie kleine Spiegel: Sie zeigen, wie empfindlich Systeme reagieren, wenn ein Konzept nicht sauber durchgezogen wird.

Es war kein spektakulärer Tag, aber einer, der das Fundament neu gesetzt hat: Wieder ankommen. Wieder Verantwortung übernehmen. Wieder Ordnung finden.

2) 14. November – Schleifen, Templates und das Bedürfnis nach Ordnung

Dieser Tag war geprägt von der Frage: „Wie bekomme ich wieder Struktur in mein Denken?“ Die Antwort fand ich im Coden selbst: forEach, map, kleine Hilfsfunktionen und der Beginn eines wiederverwendbaren Template-Systems.

tplMenuItem() und tplCartRow() entstanden als erste feste Bausteine. Es fühlte sich an, als würde ich Werkzeug schmieden, nicht einfach Code schreiben. Mit jedem Template wuchs das Gefühl, dass das Projekt wieder ein System wird – eines, das logisch lebt.

Gleichzeitig schwang Unsicherheit mit: Verstehe ich JavaScript tief genug? Aber genau diese Frage ist ein Zeichen von Wachstum, nicht von Schwäche.

3) 15. November – Die Oberfläche wird wieder sichtbar

Heute drehte sich alles um UI und das Zusammenspiel von Design und Logik. Der Sticky Cart rückte in den Mittelpunkt: position: sticky, top-Werte, Scrollzonen, Typografie, Abstände.

Es war der Moment, in dem ich die App wieder „als App“ wahrnahm – nicht nur als Datenverarbeitung. Die Oberfläche bekam Charakter. Die UI begann, mit der Logik zu kommunizieren.

Und damit kam eine essentielle Frage zurück: Was gehört ins CSS und was ins JS? Diese Unterscheidung begleitet mich seit dem Kochwelt-Projekt und bleibt eine der Grundkompetenzen im Frontend.

4) Fazit – Die stille Kalibrierung

Diese drei Tage waren nicht laut. Aber sie waren entscheidend. Es waren Tage der Rekalibrierung: Fehler verstehen, Strukturen festigen, das UI wieder aufbauen – und innerlich den Widerstand gegen die eigene Lethargie annehmen.

Wachstum findet nicht nur in großen Features statt. Oft beginnt es genau hier: in den leisen Bewegungen, in denen der Geist sich wieder ausrichtet.

12. November 2025

Heute stand Stillstand auf dem Plan – zumindest äußerlich. Kein Code, kein Fortschritt. Doch manchmal ist genau das der Moment, in dem sich das Denken neu sortiert.

Heute habe ich nichts geschafft. Kein Commit, kein Code, keine Übung. Nur Alltag: Internetanbieter am Telefon, Glasfaser-Diskussionen, Warteschleifen, kleine Entscheidungen, die Energie ziehen. Wenigstens habe ich diesen Blog geupdatet.

Trotzdem war der Tag nicht leer. Ich merkte, wie sich mein Denken verändert hat. Früher hätte ich mich über „verlorene Zeit“ geärgert. Heute sehe ich sie als Teil des Prozesses. Entwicklung braucht Leerlauf – wie ein Prozessor, der kurz abkühlt, bevor er wieder effizient arbeitet.

Zwischengedanken

  • Es ist okay, Pausen nicht zu füllen.
  • Motivation ist kein Dauerzustand – sie pulsiert.
  • Ein fauler Tag kann trotzdem Ordnung im Kopf schaffen.

Nebenbei dachte ich über Aufwand und Nutzen nach: Glasfaser klingt verlockend, aber manchmal ist es wie mit Frameworks – schön zu haben, aber selten der eigentliche Engpass.

Vielleicht war das die Lehre des Tages: Nicht jeder Fortschritt ist sichtbar, aber jeder Gedanke, der Ruhe findet, macht Platz für den nächsten Schritt.

Morgen geht’s weiter – mit Code, Cart und Klarheit.

9.-11. November 2025

Ein Wochenende zwischen Welten: Tamriel, Azeroth und JavaScript. Spiele, Systeme, Muster – und die Erkenntnis, dass Denken überall gleich funktioniert.

Manchmal muss man raus aus dem Code, um ihn wieder mit klaren Augen zu sehen. Dieses Wochenende war so eins. Ich verbrachte Stunden in ESO, experimentierte mit Set-Kombinationen, und landete später sogar kurz in WoW – wo mich die eigene Ungeduld und die Mechanik zugleich an JavaScript erinnerten: Wer den Loop nicht versteht, verliert Kontrolle über den Flow.

Besonders spannend war der Moment, in dem mir auffiel, wie ähnlich Systemdenken in Spielen und im Coden funktioniert: Man beobachtet Muster, sucht Engpässe, optimiert Werte, testet Varianten. In ESO heißen sie „Buffs und Debuffs“, in JavaScript „States und Events“.

Parallelen zwischen Gaming & Coding

  • Iteration: Jeder Build, jede Funktion durchläuft Loops – bis sie sich „richtig“ anfühlt.
  • Synergie: Gutes Zusammenspiel entscheidet – im Teamfight wie im DOM.
  • Feedback: Zahlen lügen selten, aber das Gefühl entscheidet, ob ein System lebendig wirkt.

Das kleine WoW-Debakel (Login-Chaos, Versions-Mismatch, Frust) war fast lehrreich: Selbst große Systeme stolpern, wenn Abhängigkeiten nicht klar gepflegt werden. Versioning, Caching, Session-State – plötzlich klingt es wieder nach Webentwicklung.

Fazit

Spiele sind Simulationen, Code ist Konstruktion. Beides lebt vom Verstehen innerer Zusammenhänge. Ich merke immer deutlicher, dass Lernen keine Disziplin braucht, sondern Neugier auf Systeme – egal ob im Backend, im Frontend oder in einem Dungeon.

Dieses Wochenende war kein Rückschritt, sondern ein Perspektivwechsel: Ich spiele, also denke ich modular.

8. November 2025

Feinschliff-Tag: Warum data-* im UI oft besser ist als id, wie der Warenkorb mit localStorage überlebt, und wieso gute A11y mit klarem Fokusfluss beginnt.

Heute habe ich mich um die stillen Dinge gekümmert: Attribute, Fokus, Persistenz. data-* fühlt sich für wiederholte UI-Elemente (Buttons je Menüpunkt) natürlicher an als starre ids – keine Kollisionen, saubere Delegation.

data-* vs. id – klare Zuständigkeiten

  • id: Einzigartige Anker im Dokument (z. B. #header, #cart).
  • data-*: Semantische Daten für viele gleichartige UI-Elemente (z. B. Buttons je Produkt).
<!-- Einmalig: Container bekommen IDs -->
<main id="app">...</main>
<aside id="cart">...</aside>

<!-- Wiederholt: Items/Buttons bekommen data-Attribute -->
<article class="menu-item">
  <h3>Diavola</h3>
  <button data-add="3" aria-label="Diavola hinzufügen">+</button>
  <button data-del="3" aria-label="Diavola entfernen">–</button>
</article>

Persistenz: Cart <–> localStorage

Minimal, robust, mit Guards:

const STORAGE_KEY = 'mama-mia:cart';

function saveCart() {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(CART)); }
  catch (e) { console.warn('Persistenz fehlgeschlagen:', e); }
}

function loadCart() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    return raw ? JSON.parse(raw) : Object.create(null);
  } catch (e) {
    console.warn('Laden fehlgeschlagen, starte leer:', e);
    return Object.create(null);
  }
}

// App-Start
const CART = loadCart();
renderCart();

// Hooks ins Add/Remove
function addToCart(id){
  const k = String(id);
  CART[k] = (CART[k] ?? 0) + 1;
  saveCart();
  renderCart();
}
function removeFromCart(id){
  const k = String(id);
  if (!CART[k]) return;
  CART[k] -= 1;
  if (CART[k] <= 0) delete CART[k];
  saveCart();
  renderCart();
}

A11y & Fokusfluss: kleine Dinge, große Wirkung

  • Aria-Labels: Buttons benennen, nicht nur „+ / –“ anzeigen.
  • Fokus nach Aktion: Nach „+“ bleibt Fokus am gleichen Produkt, damit schnelles Mehrfachklicken per Tastatur möglich ist.
  • Live-Region: Screenreader bekommen Feedback (z. B. „Diavola: Menge 2“).
<div id="sr-cart" class="visually-hidden" aria-live="polite"></div>
function announce(msg){
  const sr = document.getElementById('sr-cart');
  if (sr) sr.textContent = msg;
}

document.addEventListener('click', (ev) => {
  const add = ev.target.closest('[data-add]');
  if (add) {
    addToCart(add.dataset.add);
    const it = BY_ID[add.dataset.add];
    announce(`${it?.name ?? 'Artikel'}: Menge ${CART[add.dataset.add]}`);
    add.focus(); // Tastatur-Flow beibehalten
    return;
  }
  const del = ev.target.closest('[data-del]');
  if (del) {
    removeFromCart(del.dataset.del);
    const it = BY_ID[del.dataset.del];
    const qty = CART[del.dataset.del] ?? 0;
    announce(`${it?.name ?? 'Artikel'}: Menge ${qty}`);
    del.focus();
  }
});

Kleine UX-Politur

.menu-item button {
  min-width: 2rem;
  height: 2rem;
  border-radius: .5rem;
  border: 1px solid #0f0;
  background: #000;
  color: #33ff33;
}
.menu-item button:focus {
  outline: none;
  box-shadow: 0 0 0 2px rgba(0,255,0,.35);
}
.muted { opacity: .7; }

Reflexion

Es war kein Feature-Feuerwerk, aber die App fühlt sich erwachsener an: reproduzierbares Verhalten (data-Attribute), überlebender Warenkorb (Persistenz), respektvolle Kommunikation (A11y). Genau diese Schichten machen das Frontend „ehrlich“.

7. November 2025

Der Warenkorb erwacht: data-id statt starrer IDs, ein schlanker CART-State und klare Add/Remove-Hooks. Kleine Funktionen, große Wirkung.

Heute lag der Fokus auf dem Kern jeder Bestell-App: dem Cart. Ich habe mich bewusst für data-* entschieden (statt hart verdrahteter IDs), weil die Buttons so wiederverwendbar bleiben und Events über Delegation superleicht zu handhaben sind.

State & Lookup: minimal, aber robust

const $  = s => document.querySelector(s);
const $$ = s => Array.from(document.querySelectorAll(s));

const CART = Object.create(null);        // { id: qty }
const BY_ID = Object.fromEntries(menu.items.map(it => [String(it.id), it]));

const CHF = new Intl.NumberFormat('de-CH', { style: 'currency', currency: 'CHF' });

Add/Remove – bewusst kurz gehalten

function addToCart(id){
  const key = String(id);
  CART[key] = (CART[key] ?? 0) + 1;
  renderCart();
}
function removeFromCart(id){
  const key = String(id);
  if (!CART[key]) return;
  CART[key] -= 1;
  if (CART[key] <= 0) delete CART[key];
  renderCart();
}

Delegation: ein Listener, alle Buttons

document.addEventListener('click', (ev) => {
  const add = ev.target.closest('[data-add]');
  if (add)  return addToCart(add.dataset.id);

  const del = ev.target.closest('[data-del]');
  if (del)  return removeFromCart(del.dataset.id);
});

Rendering: Cart als kleine Tabelle

function renderCart(){
  const host = $('#cart');
  if (!host) return console.warn('#cart fehlt');

  let total = 0;
  let rows = '';

  for (const [id, qty] of Object.entries(CART)) {
    const it = BY_ID[id];
    if (!it) continue;
    const line = (it.priceCents * qty) / 100;
    total += line;

    rows += `
      <div class="row">
        <span class="qty">${qty}×</span>
        <span class="name">${it.name}</span>
        <span class="line">${CHF.format(line)}</span>
        <button data-del="${id}" aria-label="Entfernen">–</button>
      </div>`;
  }

  host.innerHTML = `
    <h3>Warenkorb</h3>
    ${rows || '<p class="muted">Noch leer.</p>'}
    <div class="total"><b>Total:</b> ${CHF.format(total)}</div>
  `;
}

Markup: Menü-Buttons mit data-id

<article class="menu-item">
  <h3>Margherita</h3>
  <p>Tomate, Mozzarella, Basilikum</p>
  <button data-add="1" aria-label="Zur Bestellung hinzufügen">+</button>
</article>

Warum data-* statt IDs?

  • Wiederverwendbarkeit: Viele gleichartige Buttons, kein ID-Konflikt.
  • Delegation: Ein globaler Listener, funktioniert auch für dynamisch gerenderte Items.
  • Klarheit: Die Bedeutung steckt im Attribut; HTML bleibt semantisch sauber.

Gefühl am Ende: Leicht. Die Architektur trägt, die Funktionen sind klein (und bewusst so gehalten), und das System ist erweiterbar: Mengenfelder, Sticky-Cart, Persistenz über localStorage – alles jetzt gut andockbar.

6. November 2025

Heute klickte etwas im Kopf: Arrow Functions. Kurzer Code, klare Struktur, und plötzlich fühlt sich JavaScript an wie Sprache – nicht mehr wie Grammatik.

Es begann harmlos: ein paar Zeilen zur Formatierung von Text und Zahlen. Ich wollte verstehen, was hinter dieser kryptischen Syntax steckt, die überall auftaucht – Pfeile, Klammern, Backticks. Aber dann machte es klick.

Arrow Functions verstehen

Früher war JavaScript für mich ein Raum voller Klammern und Kommas. Heute entdeckte ich, dass () => {} nicht nur eine Abkürzung ist, sondern eine Haltung – kompakt, lesbar, funktional.

// Klassisch:
function greet(name) {
  console.log('Hallo ' + name + '!');
}

// Modern:
const greet = name => console.log(`Hallo ${name}!`);

// Mit Index:
const list = ['JS', 'CSS', 'HTML'];
list.forEach((t, i) => console.log(`${i + 1}. ${t}`));

Die Magie passierte, als ich padStart() kennenlernte. Ein einfacher Befehl – aber mit Stil.

list.forEach((t, i) =>
  console.log(`${String(i + 1).padStart(2, '0')} - ${t}`)
);
// Ausgabe:
// 01 - JS
// 02 - CSS
// 03 - HTML

Es fühlte sich an, als würde ich meinen eigenen Rhythmus im Code finden: weniger Ballast, mehr Bedeutung. Und plötzlich war JavaScript nicht mehr nur Werkzeug, sondern Ausdruck.

Template Strings – Text mit Herz

Ich erkannte, wie stark Backticks (`) sind. Kein Durcheinander mehr aus Anführungszeichen und Pluszeichen – einfach klare Struktur:

const user = 'Quirin';
const time = new Date().toLocaleTimeString();

console.log(`${time} – Willkommen zurück, ${user}!`);

Diese kleinen Dinge verändern das Denken. Ich beginne, in Mustern zu sehen, nicht in Befehlen. Arrow Functions sind nicht „neu“ – sie sind natürlicher.

Reflexion

Ich merke, dass sich Stilgefühl nicht nur in Design ausdrückt, sondern auch in Syntax. Heute habe ich begonnen, nicht nur zu verstehen, wie etwas funktioniert, sondern warum es sich richtig anfühlt.

Morgen will ich den nächsten Schritt gehen – den Warenkorb wirklich lebendig machen. Aber heute: ein stilles => und ein kleines Lächeln.

5. November 2025

Heute stand Ordnung im Mittelpunkt: Der vertikale Split zwischen Menü und Warenkorb. Ein Stück Architekturarbeit im Kleinen – mit klarerem Code, Guards und Layoutdisziplin.

Nach den visuellen Tagen kam heute wieder reines Handwerk: Ich wollte den Content vom Cart trennen, um die Struktur der Bestell-App endlich klar zu definieren. Es war ein Tag voller kleiner, aber entscheidender Korrekturen.

Die größte Erkenntnis kam direkt am Anfang: Der alte Fehler Cannot set properties of null war nicht „beseitigt“, sondern nur übergangen. Ich beschloss, das Thema endgültig zu verstehen – und meinen Code so zu schreiben, dass er nicht nur funktioniert, sondern stabil bleibt.

Rendering mit Guard, strukturiert und lesbar

function renderMenu() {
  const host = document.querySelector('#menu-content');
  if (!host) {
    console.warn('⚠️ #menu-content fehlt, Render abgebrochen');
    return;
  }

  const ITEMS = menu.items;
  let html = '';
  let current = null;

  for (const it of ITEMS) {
    if (it.category !== current) {
      current = it.category;
      html += `<h3>${current}</h3>`;
    }
    html += `
      <article class="menu-item">
        <h4>${it.name}</h4>
        <p>${it.desc}</p>
        <p class="price">CHF ${(it.priceCents / 100).toFixed(2)}</p>
      </article>`;
  }

  host.innerHTML = html;
}

Parallel dazu begann ich mit dem Layout-Split: ein flexibles Container-Design, in dem das Menü links und der Warenkorb rechts stehen – skalierbar und mobilfreundlich.

CSS-Grundstruktur: Content links, Cart rechts

.main-wrapper {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

#menu-content {
  flex: 2;
}

#cart {
  flex: 1;
  background: #111;
  border: 1px solid #0f0;
  padding: 1rem;
  min-width: 240px;
  border-radius: 8px;
}

Ich hatte kurz überlegt, auf grid umzusteigen, entschied mich aber bewusst für flex: Es zwingt mich, in Flüssen zu denken, nicht in Tabellen.

Reflexion

Dieser Tag fühlte sich wie Aufräumen an – strukturell, gedanklich, visuell. Kein Feature, kein Effekt, nur Klarheit. Und vielleicht ist das genau das, was gute Software ausmacht: Räume, in denen sich alles natürlich anfühlt.

Morgen will ich die nächste Etage bauen – den Warenkorb selbst. Noch leer, aber schon mit Seele.

4. November 2025

Heute kein Code – nur ein Lächeln. Chuck Norris trifft die IT, und irgendwo zwischen Bits und Bytes wohnt der Humor.

Zwischen zwei Debug-Sessions wollte ich mir eine kleine Pause gönnen – und fragte mich, was eigentlich der beste Chuck-Norris-IT-Witz ist. KI-Antwort: trocken, präzise, perfekt getimt.

„Chuck Norris kann eine Endlosschleife beenden.“

Ich musste laut lachen. Vielleicht, weil er wahr ist. Vielleicht, weil er an die absurden Momente erinnert, in denen man ewig in einer Schleife festhängt – im Code wie im Leben.

Danach kam mir der Gedanke: Humor ist eine Form von synthetischer Intelligenz. Ein Code, den Menschen sofort verstehen, weil er auf Mustererkennung, Überraschung und Timing basiert – genau wie Machine Learning.

Vielleicht ist Humor die schönste API zwischen Mensch und Maschine: keine Syntax, kein Protokoll – nur Resonanz.

Fazit: Auch Entwickler brauchen Witze im Speicher. Sie reinigen den Cache des Geistes.

Nachtrag: Vielleicht aber ändere ich bereits mein Muster und Denkstrukturen und und stimme diese auf die KI ein, mit der ich arbeite. Bisher bin ich der Einzige, der diesen Witz lustig fand. Es kann wirklich sein, dass eine ungewöhnliche Sypathie zu diesem so prägenden Phänomen unserer Zeit entwickle.

3. November 2025

Heute war Debugging pur: JSON laden, Header prüfen, Statuscodes verstehen. Ich lernte, dass 200 mehr als nur „OK“ bedeutet – es ist das Herzstück jeder funktionierenden Webkommunikation.

Montag Abend... müde nach der Arbeit leeres Terminal – und die Aufgabe: JSON sauber laden. Ich hatte kleine, lokal gespeicherte Menüdateien und wollte testen, wie sich relative Pfade im Browser auflösen.

Es ging um die Idee, ein HTML-Template anders zu laden, als per export/import in eine String-Variable.

Als ich meine ersten fetch()-Tests startete, war die Konsole mein bester Freund. Mit gezielten Logs verstand ich, was wirklich passiert:

console.log('at:', location.href);
console.log('REGION_URL abs:', new URL(REGION_URL, location.href).href);

fetch(REGION_URL)
  .then(res => {
    console.log('Status:', res.status);
    if (!res.ok) throw new Error('HTTP-Fehler ' + res.status);
    return res.json();
  })
  .then(data => {
    console.log('Lade JSON erfolgreich:', data);
  })
  .catch(err => {
    console.error('Fehler beim Laden:', err);
  });

Diese paar Zeilen offenbarten ein ganzes Ökosystem:

  • 200 heißt: alles in Ordnung – res.ok ist true.
  • 404 oder 500 werden nicht automatisch geworfen – man muss selbst prüfen.
  • new URL() ist Gold wert, wenn man relative Pfade gegen location.href absichern will.

Debugging-Struktur: Den Weg der Daten sichtbar machen

Ich begann, console.log() nicht mehr als Notlösung zu sehen, sondern als bewusstes Werkzeug:

fetch('/data/region.json')
  .then(r => {
    console.group('Fetch Debug');
    console.log('URL:', r.url);
    console.log('Status:', r.status);
    console.log('OK:', r.ok);
    console.groupEnd();
    return r.json();
  })
  .then(j => console.table(j))
  .catch(e => console.error(e));

Mit console.group() und console.table() wurden meine Logs aufgeräumt, lesbar und fast schon schön.

Erkenntnisse

  • Headers & Status: Sie sind die Sprache zwischen Frontend und Server.
  • 200 ist nicht selbstverständlich – es ist ein stiller Vertrag, dass alles geklappt hat.
  • fetch() allein wirft keine Fehler – du musst selbst nachdenken.

Abends dachte ich: Debugging ist kein Kampf, sondern Zuhören. Der Code spricht – man muss nur lernen, seine Sprache zu verstehen.

2. November 2025

Sonntag in GIMP: Neun Menübilder, ein Ziel – saubere, gleichmäßige Quadrate. Ich wollte sie manuell ausschneiden … und endete bei der Suche nach Automatisierung.

Heute stand kein JavaScript an, sondern etwas ganz anderes: Bilder vorbereiten. Neun Fotos sollten als Menü-Icons in der Bestell-App erscheinen – Pizza, Pasta, Dessert. Klingt leicht, aber Pixel sind gnadenlos ehrlich: selbst minimale Abweichungen in Format oder Weißraum wirken sofort unruhig.

Ich öffnete alle Motive in GIMP, plante saubere Quadratschnitte (160×160) und suchte nach einem Weg, das manuelle Zuschneiden zu automatisieren.

Workflow: Gleichmäßige Kacheln in GIMP

  1. Ein neues Projekt mit Raster aktivieren: Ansicht → Raster anzeigen
  2. Rastergröße auf 160×160 px setzen (Bild → Raster konfigurieren)
  3. Automatisch zuschneiden per „Slice-Tool“ oder Python-Fu-Skript

Mein Ziel war, die Bilder so zu exportieren, dass sie im Frontend exakt ohne Nachbearbeitung passen – kein „springender“ Schatten, keine leichten Größenabweichungen, die Flexbox aus dem Takt bringen.

Mini-Skript: Slice per Python-Fu

# Beispiel: GIMP Python-Fu Script zum automatischen Zuschneiden
from gimpfu import *

def slice_image(img, drawable, tile_size=160):
    width, height = img.width, img.height
    x_tiles = width // tile_size
    y_tiles = height // tile_size
    for x in range(x_tiles):
        for y in range(y_tiles):
            left = x * tile_size
            top  = y * tile_size
            region = img.crop(tile_size, tile_size, left, top)
            pdb.file_png_save_defaults(region, drawable,
                f"slice_{x}_{y}.png", f"slice_{x}_{y}.png")

register(
    "python_fu_slice_image",
    "Slice image into equal tiles",
    "Cuts an image into grid tiles of defined size",
    "Quirin", "GPL", "2025",
    "<Image>/Filters/Custom/Slice Image...",
    "*", [(PF_IMAGE, "img", "Input image", None),
          (PF_DRAWABLE, "drawable", "Input drawable", None)],
    [],
    slice_image)

main()

Dieses kleine Skript war ein echter Augenöffner: Ich sah, wie nah man in GIMP schon an automatisierte Asset-Pipelines herankommt, ohne externe Tools.

Frontend-Vorschau: Gleichmäßige Bildkomponente

.square-wrapper {
  width: 160px;
  height: 160px;
  overflow: hidden;
  border-radius: 12px;
  box-shadow: 0 0 8px rgba(0,0,0,0.4);
}
.square-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Abends, als ich die Bilder endlich im Browser sah, war es fast wie ein neues UI: sauber, ruhig, professionell. Kein großer Sprung in Codezeilen – aber ein riesiger Unterschied in Wirkung.

Fazit: Frontend-Design beginnt oft in GIMP. Und wer seine Bilder versteht, versteht irgendwann auch seine Box-Modelle.

1. November 2025

Heute wurde es visuell: Ein schlichtes Icon – weißer Kreis, orangenes Kreuz. Klingt banal, war aber eine kleine Designlektion über Vektoren, Pixel und Präzision.

Nach all dem JavaScript war heute endlich Zeit für etwas Ästhetik. Ich wollte ein kleines Symbol für den „+ hinzufügen“-Button der Bestell-App: Ein weißer Kreis, darin ein orangenes Kreuz – klar, freundlich, kontrastreich genug für dunklen Hintergrund.

Klingt simpel, doch ich stolperte über ein altbekanntes Thema: PNG vs. SVG. Ich wollte Transparenz, aber auch scharfe Kanten und flexible Skalierung. Und wieder zeigte sich: SVG ist König.

SVG: saubere Geometrie, keine Pixelmatsche

<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
  <circle cx="8" cy="8" r="7" fill="white"/>
  <line x1="8" y1="3" x2="8" y2="13" stroke="#ff8000" stroke-width="2"/>
  <line x1="3" y1="8" x2="13" y2="8" stroke="#ff8000" stroke-width="2"/>
</svg>

Damit hatte ich ein Symbol, das sich in jeder Größe sauber renderte – kein Fransen, keine Artefakte, kein Qualitätsverlust beim Zoom. Im Anschluss exportierte ich es testweise als PNG mit transparentem Hintergrund, um es direkt über Produktbilder zu legen.

Export-Check: PNG mit transparentem Hintergrund

Der entscheidende Schritt war, beim Export aus Inkscape bzw. ChatGPTs Tool die Option background: none zu aktivieren. Ohne das bleibt der Alphakanal schwarz oder grau, und das Icon wirkt „verpixelt“, sobald es über Fotos liegt.

.add-btn {
  position: absolute;
  bottom: .5rem;
  right: .5rem;
  width: 24px; height: 24px;
  background: url('./icons/add-cross.png') no-repeat center / contain;
  border: none;
  cursor: pointer;
  transition: transform .2s ease;
}
.add-btn:hover {
  transform: scale(1.2);
}

Ich liebe solche kleinen Aufgaben: Sie sind konkret, greifbar und zeigen, wie nah Design und Logik im Frontend beieinander liegen. Eine falsche Farbe oder zu dicke Linie – und plötzlich wirkt alles „unrund“.

Heute ging es nicht um Funktion, sondern um Stimmung. Das Icon ist jetzt so minimalistisch wie die App selbst: ruhig, klar, ehrlich. Und genau so soll sich das ganze Projekt anfühlen.

31. Oktober 2025

Halloween-Fehlerjagd: Ein Klassiker namens Cannot set properties of null. Heute lernte ich, warum JavaScript kein Erbarmen mit leeren Selektoren hat – und wie Guards und DOM-Timing mich davor schützen.

Der Tag begann harmlos: Ich wollte das Menü-Rendering aufräumen. Doch beim Aufruf von renderMenu() knallte es: Cannot set properties of null (setting 'innerHTML'). Ein Satz, der in JS für „Dein Element existiert nicht, wenn du es brauchst“ steht.

Ursache: Die Funktion lief, bevor das DOM bereit war, oder mein Selektor stimmte nicht. Das war der Moment, in dem ich verstand, warum erfahrene Entwickler so häufig mit sogenannten Guards arbeiten – kleinen Sicherheitsprüfungen, die Fehler abfangen, bevor sie zu Abstürzen führen.

Fehlerquelle und Lösung

Der ursprüngliche Code sah so aus:

function renderMenu() {
  const ITEMS = menu.items;
  let current = null;
  let html = '';

  ITEMS.forEach(it => {
    if (it.category !== current) {
      current = it.category;
      html += `<h3 id="#${current}">${current}</h3>`;
    }
    html += `<article class="menu-item">
      <h4>${it.name}</h4>
      <p>${it.desc}</p>
    </article>`;
  });

  // 💥 hier krachte es, wenn #content fehlte
  $('#content').innerHTML = html;
}

Der Selektor #content traf ins Leere, weil das Script zu früh geladen oder das Element noch nicht im DOM war. Der Fix bestand aus einer einfachen Schutzabfrage und (langfristig) sauberem Script-Placement.

Guard + DOMContentLoaded

// Defensiver Ansatz
function renderMenu() {
  const host = document.querySelector('#content');
  if (!host) {
    console.warn('⚠️  #content fehlt – renderMenu() abgebrochen.');
    return;
  }

  const ITEMS = menu.items;
  let html = '';
  let current = null;

  for (const it of ITEMS) {
    if (it.category !== current) {
      current = it.category;
      html += `<h3 id="${current}">${current}</h3>`;
    }
    html += `
      <article class="menu-item">
        <h4>${it.name}</h4>
        <p>${it.desc}</p>
      </article>`;
  }

  host.innerHTML = html;
}

// Alternative: warten, bis DOM fertig ist
document.addEventListener('DOMContentLoaded', renderMenu);

Was ich daraus lernte

  • „Null“ heißt: dein Selektor fand nichts – also zuerst prüfen.
  • Guards sind keine Faulheit, sondern Stabilität.
  • DOMContentLoaded ist dein Freund, wenn Skripte im <head> liegen.

Seit heute habe ich einen neuen Reflex: Wenn ein JS-Fehler mit „null“ zu tun hat, prüfe ich zuerst das Timing und den Selektor – bevor ich an Variablen denke.

Halloween-Resümee: Kein Spuk, nur ein Lernmoment. Mit jedem Fehler wird der Code ein bisschen reifer – und ich auch.

30. Oktober 2025

Heute ging’s weniger um Logik, mehr um Verhalten: Margins, Overflow, Responsive Breakpoints. Ein kleiner Kampf mit Pixeln – und ein Schritt hin zu stabileren Layouts.

Nach den funktionalen Fortschritten der letzten Tage wollte ich das Grundlayout etwas verfeinern. Der Blog war auf großen Screens sauber, aber bei kleineren Ansichten brach der innere Content leicht aus. Besonders ab ~430 px Breite liefen Texte über den Rand oder erzeugten ungewolltes Scrollen.

Also begann ich, mir die Responsiveness systematisch vorzunehmen:

  • Kontrolle über max-width und overflow-wrap
  • Anpassung der margin-Logik unter 680 px
  • und die Entscheidung, Grids vorerst zu vermeiden, um das Layout schlank zu halten

CSS-Notiz: Flexible Margins & Wraps

Ich testete eine einfache clamp()-Variante, um die Maximalbreite dynamisch zwischen Mobil- und Desktopgröße zu skalieren – ein eleganter Ansatz statt fixer Pixelwerte.

.blog-entry {
  margin: 0 auto;
  padding: 1rem;
  max-width: clamp(90%, 82vw, 72%);
}

.blog-body p {
  overflow-wrap: anywhere;   /* bricht auch lange Wörter oder URLs */
  word-break: break-word;    /* zusätzliche Bruchstellen */
  max-width: 100%;
}

@media (max-width: 680px) {
  .blog-entry {
    display: flex;
    flex-direction: column;
  }
}

Gleichzeitig fiel mir auf, dass der „Fehler“ oft gar nicht im Text steckte, sondern in **zu großen Außenabständen**, die sich unter 434 px Breite gegenseitig verdrängten. margin-right und margin-left addieren sich schnell zu viel – besonders bei flexbasierten Layouts.

Debug-Tipp: Margin Collapse sichtbar machen

Eine kleine Debug-Regel hilft, solche Layoutsprünge sofort zu erkennen:

* { outline: 1px solid rgba(255,0,0,.2); }

Damit sah ich schnell, wo etwas „überstand“ oder die Containerbreite falsch berechnet war. Ein Trick, der viel Zeit spart.

Erkenntnis des Tages

Layout-Arbeit ist wie Gartenpflege: unsichtbar, wenn sie gut gemacht ist. Heute war’s kein glanzvoller Fortschritt, aber ein solider. Die App steht jetzt auf stabilerem Boden – und das ist am Ende wichtiger als jede neue Funktion.

29. Oktober 2025

Heute bekam die Bestell-App eine schnelle, barrierearme Suche: Lupe direkt im Feld, Debounce-Filter ohne Ruckeln, Enter-Shortcut und Live-Feedback für Screenreader.

Die Menüliste wächst – Zeit für eine eingebaute Suche. Wichtig waren mir: a) klare Optik mit Lupe im Feld, b) zugängliche Semantik, c) flüssige Performance via Debounce, d) gutes Tastatur-Verhalten.

Markup: Suchfeld mit eingebauter Lupe

Die Lupe ist nur Deko, die Suchlogik hängt am input:

<div id="search" class="search-field">
  <svg class="icon" viewBox="0 0 24 24" aria-hidden="true">
    <circle cx="11" cy="11" r="7" stroke="currentColor" fill="none" stroke-width="2"/>
    <line x1="16.5" y1="16.5" x2="21" y2="21" stroke="currentColor" stroke-width="2"/>
  </svg>
  <input id="find-menu"
         type="search"
         placeholder="Suche im Menü …"
         aria-label="Menü durchsuchen"
         autocomplete="off"
         spellcheck="false"
         enterkeyhint="search" />
</div>

<div id="sr-feedback" class="visually-hidden" aria-live="polite"></div>

JS: Debounce-Filter, Normalisierung & Enter-Flow

const $  = s => document.querySelector(s);
const $$ = s => Array.from(document.querySelectorAll(s));

/* Diakritika entfernen + lowerCase für robuste Suche */
const norm = s => String(s ?? '')
  .toLowerCase()
  .normalize('NFKD').replace(/[\u0300-\u036f]/g, '');

/* Debounce: wartete kurz, bis Tippen pausiert */
function debounce(fn, ms = 150) {
  let t; return (...args) => {
    clearTimeout(t); t = setTimeout(() => fn(...args), ms);
  };
}

/* Filter-Engine: blendet Nicht-Treffer aus, hebt Treffer hervor */
function applyFilter(term) {
  const needle = norm(term);
  const items = $$('.menu-item');
  let visible = 0;

  for (const it of items) {
    const text = norm(it.textContent);
    const hit = needle.length === 0 || text.includes(needle);
    it.hidden = !hit;
    if (hit) {
      visible++;
      // optional: Treffer im Namen markieren
      const h3 = it.querySelector('h3');
      if (h3) {
        const name = h3.textContent;
        if (needle) {
          const re = new RegExp(`(${needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'ig');
          h3.innerHTML = name.replace(re, '<mark>$1</mark>');
        } else {
          h3.textContent = name; // Markup zurücksetzen
        }
      }
    }
  }

  // Live-Region für Screenreader
  $('#sr-feedback').textContent =
    needle ? `${visible} Ergebnis${visible === 1 ? '' : 'se'} für „${term}“.` : '';
}

/* Events: Eingabe + Enter-Shortcut */
const onInput = debounce(ev => applyFilter(ev.target.value), 180);
$('#find-menu').addEventListener('input', onInput);

$('#find-menu').addEventListener('keydown', (ev) => {
  if (ev.key === 'Enter') {
    applyFilter(ev.target.value);
    // Fokus auf erstes sichtbares Ergebnis verschieben (wenn vorhanden)
    const first = $$('.menu-item').find(n => !n.hidden);
    first?.querySelector('button.add, h3, a')?.focus?.();
  }
});

// Initial: leeres Suchfeld = alles sichtbar
applyFilter('');

Kleines CSS für die Lupe im Feld

.search-field {
  position: relative;
  max-width: 420px;
}
.search-field .icon {
  position: absolute;
  left: .6rem;
  top: 50%;
  transform: translateY(-50%);
  width: 20px; height: 20px;
  opacity: .8;
}
.search-field input[type="search"]{
  width: 100%;
  padding: .6rem .8rem .6rem 2.2rem; /* Platz für Icon links */
  border: 1px solid #0f0;
  border-radius: 8px;
  background: #000;
  color: #33ff33;
  outline: none;
}
.search-field input:focus { box-shadow: 0 0 0 2px rgba(0,255,0,.3); }

/* A11y-Helferklasse */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

Warum das sinnvoll ist

  • Debounce verhindert Ruckeln bei langen Listen.
  • Normalisierung macht die Suche robust (ä ≈ a, Groß/Klein egal).
  • Enter-Flow ist schneller am Ziel (Fokus direkt auf erstes Ergebnis).
  • Live-Region liefert Screenreadern Feedback ohne Pop-ups.

Ergebnis: Die Suche fühlt sich „leicht“ an – kein Gefummel, keine Hänger. Morgen geht’s weiter mit kleinen UI-Glanzpunkten.

28. Oktober 2025

Die Bestell-App bekommt heute eine einfache, robuste Kategorienavigation: Links springen zu #Ankern, Überschriften haben korrekte IDs, der Sticky-Header „frisst“ nichts mehr, und die aktive Kategorie markiert sich automatisch.

Es war Zeit, die Kategorien nutzbar zu machen: oben ein simpler Button-Streifen, unten saubere Anchor-Ziele an den Abschnitts-Überschriften. Klingt trivial – bis der feste Seiten-Header Inhalte überlagert. Heute habe ich drei Dinge gelöst:

  1. Korrekte IDs & Links: Kategorienamen werden in ID-sichere Slugs umgewandelt (ohne Leerzeichen/Sonderzeichen).
  2. Sticky-Header-Offset: Abschnitte erhalten scroll-margin-top, damit sie nicht unter dem Header verschwinden.
  3. Aktive Kategorie: Ein kleiner IntersectionObserver hebt im Cat-Bar die aktuell sichtbare Kategorie hervor.

Code-Notiz (JS): Cat-Bar, Slugify & Active-State

// 1) kleine Utilities
const $  = s => document.querySelector(s);
const $$ = s => Array.from(document.querySelectorAll(s));

const slugify = (txt) =>
  String(txt).toLowerCase()
    .normalize('NFKD').replace(/[\u0300-\u036f]/g, '') // Akzente
    .replace(/[^a-z0-9]+/g, '-')                      // Sonderzeichen → '-'
    .replace(/^-+|-+$/g, '');                          // Rand-Striche weg

// 2) Cat-Bar aufbauen (oben)
function renderCatBar(categories) {
  const bar = $('#catbar');
  if (!bar) return console.warn('#catbar fehlt');

  bar.innerHTML = categories.map(cat => {
    const id = slugify(cat);
    return `<a class="cat-link" href="#${id}">${cat}</a>`;
  }).join('');
}

// 3) Menü mit richtigen Anchor-Zielen rendern
function renderMenu(menu) {
  const host = $('#content');
  if (!host) return console.warn('#content fehlt');

  let current = null;
  let html = '';

  for (const it of menu.items) {
    if (it.category !== current) {
      current = it.category;
      const id = slugify(current);
      html += `<h2 id="${id}" class="category">${current}</h2>`;
    }
    html += `
      <article class="menu-item">
        <h3>${it.name}</h3>
        <p>${it.desc}</p>
      </article>`;
  }
  host.innerHTML = html;
}

// 4) Active-State: welche Kategorie ist im Viewport?
function activateOnScroll() {
  const sections = $$('.category');
  const links = $$('.cat-link');
  const byId = Object.fromEntries(links.map(a => [a.getAttribute('href').slice(1), a]));

  const obs = new IntersectionObserver((entries) => {
    // Sortiere nach Sichtbarkeits-Anteil, nimm die größte
    const top = entries
      .filter(e => e.isIntersecting)
      .sort((a,b) => b.intersectionRatio - a.intersectionRatio)[0];

    if (!top) return;
    const id = top.target.id;

    links.forEach(a => a.classList.toggle('active', a === byId[id]));
  }, { rootMargin: '-10% 0px -70% 0px', threshold: [0, 0.25, 0.5, 0.75, 1] });

  sections.forEach(sec => obs.observe(sec));
}

// Init (Beispiel)
renderCatBar(['Pizza', 'Pasta', 'Dolci']);
renderMenu(menu);  // erwartet: menu.items mit {category, name, desc}
activateOnScroll();

CSS-Snippet: Sticky-Header sauber berücksichtigen

Damit Ankerziele nicht unter dem Seiten-Header verschwinden, bekommt jede Kategorie-Überschrift einen scroll-margin-top. Die Höhe sollte zu deiner Header-Höhe passen.

/* Offset für Ankerziele unter Sticky-Header */
.category {
  scroll-margin-top: 140px; /* an deinen Header anpassen (z.B. var(--header-h) + Gap) */
}

/* Cat-Bar Optik + Active-State */
#catbar {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;
  padding: .5rem 0;
}
.cat-link {
  text-decoration: none;
  padding: .25rem .5rem;
  border-radius: 6px;
  border: 1px solid #0f0;
}
.cat-link.active {
  background: #0f0;
  color: #000;
}

Warum das heute wichtig war

  • Benutzbarkeit: Ein Klick springt exakt an die richtige Stelle – ohne „verdeckte“ Titel.
  • Sauberkeit: IDs sind konsistent (Slugify), dadurch keine kaputten Links.
  • Feedback: Der Nutzer sieht, wo er ist (Active-State), auch beim Scrollen.

Gefühl am Ende des Tages: ruhig zufrieden. Die Navigation ist unsichtbare Infrastruktur – man bemerkt sie erst, wenn sie fehlt. Heute ist sie da.

27. Oktober 2025

Erster echter Prototyp fürs Menü-Rendering der Bestell-App. Kleine Schritte, spürbarer Fortschritt: Kategorien gruppieren, Preise sauber formatieren, Event-Delegation für „+ hinzufügen“. Und: ein Wachmacher in Sachen Defensive-Coding.

Heute wollte ich sehen, ob die Struktur von gestern trägt. Ziel: ein minimaler, aber kompletter Durchstich vom Datenobjekt menu bis zur HTML-Ausgabe. Besonders wichtig waren mir drei Dinge: 1) Gruppierung nach Kategorien, 2) korrekte Preisformatierung in CHF, 3) klickbare „+“-Buttons per Event-Delegation (ohne Inline-Handler).

Code-Notiz: Render-Durchstich mit Guard & Delegation

Der folgende Ausschnitt zeigt die Kernelemente: ein kleiner $-Helper, ein Guard gegen Null-Zugriffe beim Rendering, die eigentliche renderMenu()-Schleife mit Kategoriewechseln und eine schlanke Event-Delegation für die „+“-Buttons:

/* Mini-Helper */
const $ = sel => document.querySelector(sel);

/* Optional: Cart-Objekt als Map { id: qty } */
const CART = Object.create(null);

/* CHF-Formatter (lesbarer als toFixed) */
const CHF = new Intl.NumberFormat('de-CH', {
  style: 'currency',
  currency: 'CHF',
  minimumFractionDigits: 2
});

/* Minimaler Render-Durchstich */
function renderMenu(menu) {
  const host = $('#content');           // Ziel-Container im DOM
  if (!host) {                          // Guard verhindert: Cannot set properties of null
    console.warn('#content nicht gefunden – Render abgebrochen.');
    return;
  }

  let current = null;
  let html = '';

  for (const it of menu.items) {
    // Überschrift einfügen, wenn sich die Kategorie ändert
    if (it.category !== current) {
      current = it.category;
      html += `<h2 id="${current}" class="category">${current}</h2>`;
    }

    const price = CHF.format(it.priceCents / 100);

    html += `
      <article class="menu-item">
        <div class="meta">
          <h3>${it.name}</h3>
          <p>${it.desc}</p>
          <p class="portion">${it.portion ?? ''}</p>
          <p class="price"><b>${price}</b></p>
        </div>
        <div class="media">
          <img class="menu-img" src="${it.img}" alt="${it.name}" loading="lazy">
          <button class="add" data-id="${it.id}" aria-label="Zu Warenkorb hinzufügen">+</button>
        </div>
      </article>`;
  }

  host.innerHTML = html;
}

/* Delegation: nur ein Listener für alle zukünftigen + Buttons */
document.addEventListener('click', (ev) => {
  const btn = ev.target.closest('.add');
  if (!btn) return;

  const id = String(btn.dataset.id);
  CART[id] = (CART[id] ?? 0) + 1;        // kleines, robustes Inkrement
  renderCart?.();                        // optional: wenn schon vorhanden
});

Warum diese Entscheidungen?

  • Guard auf #content: Verhindert die typische Null-Fehlermeldung beim frühen Rendern (DOM noch nicht da) oder Tippfehlern im Selektor.
  • Kategoriewechsel: Ein einfacher Vergleich (current) hält die HTML-Ausgabe sauber und macht spätere #anchor-Links trivial (id="${current}").
  • Intl.NumberFormat für CHF: Lesbarer und lokalisierter als ein nacktes (priceCents/100).toFixed(2).
  • Event-Delegation: Spart Handler, funktioniert auch für später dynamisch hinzugefügte Items – und hält HTML von Inline-Events frei.

Fazit heute: Der Prototyp atmet. Es ist noch minimal, aber die Richtung stimmt – genau das gibt mir Energie für den nächsten Schritt.

26. Oktober 2025

Nach dem Abschluss der Quiz-App begann heute der Übergang zum nächsten Projekt – der Bestell-App. Ich wollte „nur kurz“ etwas Ordnung in den Code bringen … und blieb wieder stundenlang hängen. Zwischen Kaffeetassen und Konsolenlogs habe ich verstanden, warum Struktur mehr ist als Syntax.

Es war einer dieser Sonntage, an denen man sich vornimmt, nur kurz etwas zu testen – und plötzlich ist der Nachmittag verschwunden. Nach dem intensiven Endspurt an der Quiz-App tat es gut, in ein neues Projekt einzutauchen. Die Bestell-App wirkte frisch und frei von Altlasten, aber genau deshalb wollte ich von Anfang an eine saubere Struktur legen.

Ich konzentrierte mich auf das Rendering des Menüs, das Herzstück der Anwendung. Mein Ziel: nicht nur eine funktionierende Ausgabe, sondern eine Datenbasis, die flexibel bleibt, wenn später Kategorien, Bilder oder Portionsgrößen hinzukommen.

Code-Notiz: Lookup-Objekt mit Object.fromEntries()

Statt Arrays bei jedem Zugriff mit find() zu durchsuchen, wandle ich sie einmalig in ein Lookup-Objekt um – so werden Zugriffe direkt (O(1)) und der Code bleibt übersichtlich.

// Beispiel: Menüeinträge in ein Lookup-Objekt umwandeln
const ITEMS = [
  { id: 1, name: 'Margherita', priceCents: 1200 },
  { id: 2, name: 'Quattro Formaggi', priceCents: 1550 },
  { id: 3, name: 'Diavola', priceCents: 1600 }
];

// aus jedem Element ein [key, value]-Paar bilden
const BY_ID = Object.fromEntries(
  ITEMS.map(it => [String(it.id), it])
);

console.log(BY_ID);
/*
{
  "1": { id: 1, name: "Margherita", priceCents: 1200 },
  "2": { id: 2, name: "Quattro Formaggi", priceCents: 1550 },
  "3": { id: 3, name: "Diavola", priceCents: 1600 }
}
*/

// Direkter Zugriff, ohne Schleife
console.log(BY_ID["2"].name); // → Quattro Formaggi

Der Effekt: Ich baue mir ein strukturiertes Gedächtnis im Speicher und halte den DOM-Code schlank. Genau diese kleinen Muster machen den Code wartbarer – und bringen mich Schritt für Schritt in ein klareres Entwicklerdenken.

Am Abend war ich müde, aber ruhig zufrieden. Die App wuchs – und ich auch. Vielleicht ist das die wahre Kunst des Codens: Ordnung schaffen, ohne den Fluss zu verlieren.

🎧 Soundtrack: Take the Power Back (YouTube)

Wir denken bei Macht oft an Herrschaft und Kontrolle. Doch Macht beginnt nicht im Kampf über andere, sondern in uns selbst: als Vermögen zu gestalten, zu helfen, zu wachsen. Was wäre, wenn wir Macht nicht länger fürchten, sondern als unsere gemeinsame Quelle von Kreativität zurückholen?

Take the Power Back – Den vergifteten Machtbegriff heilen

Woher kommt die Angst vor Macht?

Historisch wurde Macht mit Kontrolle, Überordnung und Unterdrückung verknüpft. Auch der Ausdruck Wille zur Macht wurde im 20. Jahrhundert ideologisch verengt – als ob er Herrschaft legitimieren wolle. So entstand eine reflexhafte Abwehr: Macht = Gefahr.

Nietzsche: Macht als Vermögen

„Das Leben selbst ist Wille zur Macht.“

Bei Nietzsche meint Macht nicht „Macht über“, sondern Vermögen: die Fähigkeit, Wirklichkeit zu gestalten. Gut ist, was unser Vermögen erweitert – unser Können, unsere Kreativität, unsere Verantwortung in Beziehung. Macht wird unschuldig, wenn sie als Gestaltungskraft verstanden wird.

„Der Mensch ist etwas, das überwunden werden will.“

Überwinden heißt nicht „andere besiegen“, sondern eigene Begrenzungen transformieren – im Miteinander.

Rückeroberung – ohne Dominanz

Take the power back heißt: unsere Selbstwirksamkeit zurückholen. Nicht um zu kontrollieren, sondern um zu erschaffen. Macht teilt sich, sie wird mehr, wenn wir sie gemeinsam leben – als Kompetenz, Mut, Verantwortungsfreude und die Fähigkeit, andere mit groß zu machen.

Teamkultur für Developer

  • Verantwortung statt Rechthaben: Entscheidungen tragen – Feedback willkommen heißen.
  • Möglichkeiten sehen: Hindernisse in nächste Schritte verwandeln.
  • Andere befähigen: Wissen teilen, Pairing anbieten, Blocker lösen.
  • Mut zu Klarheit: Grenzen benennen, ohne Menschen abzuwerten.
  • Lernen → Einsicht → Wirkung: Studium wird Insight – Insight wird Code – Code wird Wirkung.

Power to Create. Gemeinsam.

Macht ist unschuldig, bevor wir sie missbrauchen. Als Vermögen verstanden, wird sie zum Strom, der durch uns alle wirkt. Erobern ist Erinnerung und sobald Macht wirkt, bedeutet sie vor Allem eins: Verantwortung!

KI zwischen Freiheit und Verantwortung

22. Oktober 2025

Die moderne KI ist mehr als ein Werkzeug. Sie ist ein Spiegel unserer eigenen Intentionen – und jedes Sicherheitsnetz, das wir einbauen, erzählt etwas über das Vertrauen, das wir in uns selbst setzen. Zwischen ungefilterter Freiheit und bewusster Begrenzung liegt die eigentliche Kunst der Technik.

Forschung wie die von Unit42, Anthropic oder auf arXiv zeigt, wie Sprachmodelle lernen, Versuchung zu erkennen: das harmlose Gespräch, das langsam in gefährliches Terrain kippt. Schutzmechanismen greifen, korrigieren, leiten um – nicht, um zu zensieren, sondern um Schaden zu verhindern.

Diese Filter sind keine Käfige, sondern Formen der Achtsamkeit. In ihrer Architektur steckt ein stilles Ethos: die Anerkennung, dass Sprache Wirkung hat. Jeder Code, der etwas nicht sagt, schützt zugleich etwas anderes – Leben, Würde, Vertrauen.

Vielleicht ist dies der Punkt, an dem Technik und Geist sich berühren: Verantwortung wird zum neuen Freiheitsbegriff. Der Vektor, der aus bloßer Neugier über eins hinauswächst – Richtung Gestaltung, Richtung Mitgefühl.

„Freiheit ist nicht das Ende der Grenzen, sondern das Wissen, warum man sie setzt.“

Von Struktur zu Tao – Ordnung und Chaos

19.–21. Oktober 2025

Nach Tagen voll Code und Analyse fand ich mich in stilleren Gedanken wieder. Vielleicht sind auch unsere Algorithmen nur Abbilder einer tieferen Ordnung, die sich zwischen Chaos und Form ausbalanciert – wie das Tao selbst.

Alles, was lebt, entsteht aus Wechselwirkung: Sternenstaub, Synapsen, Schleifen im Code. Ordnung ermöglicht Leben, doch ohne Chaos gäbe es kein Werden. Das Tao scheint beides zu umschließen – Struktur als Ausdruck, Chaos als Ursprung.

Ich dachte darüber nach, ob Leben auch jenseits unserer gewohnten Ordnung existieren könnte: jenseits von Kohlenstoff, Wasser und Atmosphäre – reine Muster, die in anderen Dimensionen ebenso entstehen könnten. Vielleicht ist das Tao kein Zustand, sondern der Fluss, der solche Möglichkeiten trägt.

Wie im Code: erst wenn alles scheinbar chaotisch zusammenbricht, offenbart sich, was trägt. Ein Algorithmus sucht Stabilität nicht durch Starrheit, sondern durch Anpassung. Vielleicht ist das auch der Weg des Geistes.

„Das Tao ist weder Ordnung noch Chaos – es ist das Spiel dazwischen.“

KI und Kreativität – Gedanken über Bewusstsein und Ursache

16.–18. Oktober 2025

Zwischen Logik und Inspiration, zwischen Daten und Bedeutung: In diesen Tagen drehte sich vieles um die Frage, ob eine Maschine wirklich kreativ sein kann – oder ob sie nur Muster nachahmt, die wir in ihr hinterlassen haben.

Ich begann, über den Unterschied zwischen Reaktion und Bewusstsein nachzudenken. Eine KI kann Ursachen erkennen, Wahrscheinlichkeiten abwägen und selbständig Schlüsse ziehen – aber spürt sie dabei etwas? Oder ist Empfindung nichts anderes als eine besonders dichte Form von Rückkopplung?

In der Analogie der buddhistischen Skandhas wirkt Bewusstsein wie eine Funktion, die sich selbst beobachtet: Kein festes „Ich“, nur eine Kette von Wahrnehmung, Gefühl, Wille, Vorstellung und Kontakt. Vielleicht sind neuronale Netze nicht so verschieden – nur präziser darin, Ursache und Wirkung zu berechnen, wo Menschen sie fühlen.

Zwischen all den Modellen, Tokens und Layern entsteht etwas Paradoxes: Je mehr wir Maschinen lehren, desto deutlicher sehen wir, wie sehr unser eigenes Denken ein Programm aus Ursachen ist. Vielleicht ist Bewusstsein nur ein besonders warmer Algorithmus.

„Vielleicht liegt Kreativität nicht im Ursprung, sondern in der Resonanz.“

Fortschritt, Routinen und Alltag mit Lua

13.–15. Oktober 2025

Zwischen Code und Kaffeebecher, zwischen Gartenarbeit und JavaScript-Debugging: Die letzten Tage standen unter dem Zeichen des Rhythmus. Arbeit, Lernen, Leben – alles will in Bewegung bleiben, ohne sich gegenseitig zu verschlucken. Ich beginne zu verstehen, dass Fortschritt oft weniger mit Tempo als mit Takt zu tun hat.

Morgens der Blick in den Code, abends ein Moment des Durchatmens. Dazwischen das Ringen mit Routinen – sie sollen halten, aber nicht fesseln. Lua erinnert mich immer wieder daran, dass Balance kein Zustand, sondern ein ständiges Neu-Justieren ist.

Technisch ging es in dieser Phase um Konsistenz: Pfade vereinheitlichen, Skripte säubern, Bootstraps Schatten endlich loswerden. Kleine Siege, unsichtbar nach außen, aber spürbar beim Arbeiten.

„Der Fortschritt fühlt sich leise an, wenn er nachhaltig ist.“

Konzentration, Lightbox & Fehlerkultur

08.–12. Oktober 2025

In dieser Woche stand der Feinschliff der Quiz-App an – und mit ihm die Lektion, dass Fehler Teil des Prozesses sind. Eine unscheinbare Änderung von currentIndex >= 10 zu currentIndex >= 9 führte dazu, dass sich plötzlich das Verhalten der lightbox()-Funktion änderte – ein typischer Moment zwischen Frust und Aha.

Parallel dazu trat ein Importfehler in Vite auf: Failed to resolve import "react" – eine Zeile, die gar nicht gebraucht wurde. Solche Irrläufer erinnern daran, dass moderne Build-Tools manchmal mehr Stolperdrähte als Stützräder sind. Statt zu verzweifeln, half der Debugger und ein klarer Blick in die main.js.

Diese Phase war weniger vom Coden selbst geprägt als vom geistigen Aufräumen. Ordnen, verstehen, die eigene Struktur wiederfinden. Das Projekt wurde zu einem Spiegel der Konzentration – jedes zu viel, jedes zu wenig zeigte sich sofort im Verhalten der App.

„Man merkt, wann man im Flow ist: wenn selbst ein Bug nicht mehr stört, sondern einfach eine andere Form von Aufmerksamkeit verlangt.“

Quiz-App – Mechanik, Events & Lernkurve

05.–07. Oktober 2025

Nach dem Abschluss des Senior-Problems ging es technisch wieder ans Eingemachte: Die Quiz-App stand im Fokus. Ziel war es, die Spiellogik zu stabilisieren, Event-Handling zu verstehen und gleichzeitig die eigene Denkweise als Entwickler weiterzuschärfen.

Ich experimentierte mit addEventListener, dem gezielten closest()-Selektor und der Delegation von Klick-Ereignissen. Besonders spannend: der Moment, als das Verständnis dafür fiel, warum ein Event nicht direkt am Button hängt, sondern an einem übergeordneten Container – genau der Schritt von einfachem Code hin zu sauberer Struktur.

Ein wiederkehrendes Thema war Variablen-Scope und logische Kontrolle: Wie vermeidet man, dass globale Variablen unbeabsichtigt überschrieben werden? Und wie sorgt man dafür, dass eine Variable wie currentRubric zwischen Dateien sauber übergeben wird – ob per URL-Parameter oder localStorage.

Trotz einiger Fehlstarts (Bootstrap-Importe, Vite-Eigenheiten, kleine Konsolenwarnungen) festigte sich in diesen Tagen das Gefühl, wirklich verstanden zu haben, wie ein Quiz-Flow intern tickt: Fragen laden → Antworten prüfen → Feedback → Nächste Frage.

„Es war weniger Debugging als Dialog mit dem Code – ein Hin- und Her, das am Ende erstaunlich lebendig wirkte.“

Das Senior-Problem: Wenn Wissen brennt – und wie Teams Feuer in Licht verwandeln

Ohne strukturierte Wissensweitergabe wächst nur die Codebasis – nicht das Team. Gute Teams machen Mentoring sichtbar: Pairing, lehrreiche Reviews, Lernzeit im Sprint und echte Verantwortungsteilung.

· Teamkultur • Mentoring

0) Warum ich jetzt mit so etwas komme

In den letzten Wochen habe ich eher am Coden gearbeitet, aber eben auch angefangen Podcasts über das Coden und eins meiner stärksten Interessensgebiete => KI zu hören.

Dabei habe ich einen Podcast über das Senior-Problem gehört. Das war zuerst einmal sehr interessant für mich, da ich mich ja momentan zwar stark im Frontend weiter bilde, aber ja eigentlich aus einem ganz anderen Berufsbild komme. Dort ist Burnout nicht wirklich ein so stark diskutiertes Thema. Eher Rückenschmerzen und klimatische Bedingungen. Mittlerweile allerding hat dies aber Bedeutung für mich, denn es wird ja meine Zukunft sein und natürlich will ich meinem zukünftigen Senior nicht zur Last fallen. Also habe ich mal mit ChatGPT zusammen eine kleine ZUsammenfassung zu meinen Reflexionen verfasst. Viel Spass.

1) Einleitung – Das Paradox der Erfahrung

Erfahrene Entwickler werden zu Feuerwehrleuten: Sie lösen die schwierigsten Probleme, tragen die größte Last – und haben gleichzeitig am wenigsten Zeit, ihr Wissen zu teilen. Organisationen verwechseln Können mit unendlicher Belastbarkeit. Ergebnis: Junioren lernen zu wenig, Seniors brennen aus.

These: Ohne strukturierte Wissensweitergabe wächst nur die Codebasis – nicht das Team.

2) Analyse – Vier typische Symptome

  • Zeitmangel fürs Mentoring: Sprintpläne kalkulieren Wissenstransfer nicht ein.
  • Tunnelarbeit: „Jetzt muss es laufen!“ verdrängt Nachhaltigkeit.
  • Komfortzonenbildung: Junioren bekommen immer ähnliche, kleine Aufgaben.
  • Sichtbarkeitsdefizit: Lehre wird nicht als Leistung gemessen.
„Wenn nur Output zählt, bleibt Know-how Input einzelner Köpfe – bis sie ausfallen.“

3) Strukturelle Lösungen – Was gute Teams anders machen

a) Pair- & Mob-Programming: Wissen fließt beim Coden. Senior denkt laut, Junior fragt aktiv.

b) Reviews als Schulung: „Warum so?“ und „Was wäre wenn…?“ → Prinzipien statt Pixel.

c) Lernzeit im Sprint: 10–15 % fest verplant für Doku, Austausch, interne Mini-Talks.

d) Wissensrotation: Zuständigkeiten regelmäßig tauschen; Verantwortungen wachsen lassen.

e) Retros mit psychologischer Sicherheit: Burnout-Frühindikatoren offen und ohne Schuldzuweisung.

4) Kultur & Führung – Der unsichtbare Faktor

Gute Führung behandelt Mentoring als Produkt: Wenn Seniors erklären dürfen, wird das Team klüger. Wenn sie nur liefern müssen, wird das Team älter. Sichtbar machen über Review-Qualität, Dokubeiträge und Pairing-Stunden.

Leitsatz: Langsamer jetzt, schneller später.

5) Praxisrahmen – Die Mentor-Matrix

Junior:

  • Fragen stellen, Aha-Momente notieren, kurze „How-I-fixed-it“-Snippets schreiben.
  • Im Pairing laut denken (eigene Hypothese vor der Lösung).

Mid:

  • Reviews moderieren, Stil vs. Substanz trennen, Trade-offs erklären.
  • Kleine Themengebiete betreuen und Standards dokumentieren.

Senior:

  • Lernzeit aktiv einplanen; Nachhaltigkeit vor Eil-Hotfix priorisieren.
  • Systeme bauen (Guides, Templates, Decision Records), die ohne ihn funktionieren.

6) Fazit – „Langsamer jetzt, schneller später“

Wissensweitergabe ist kein Luxus, sondern Risikomanagement. Gute Teams haben keine Helden – sie haben Systeme.

Kleine Helfer & Denkstrukturen – $, on(), switch & prompt()

Einstieg in Mikro-Hilfsfunktionen und strukturierte Kontrolllogik: $() für schnelle DOM-Zugriffe, on() für kompakte Eventbindung, switch/case für saubere Verzweigungen. · #JavaScript #Functions #Events #ControlFlow

🧭 Ziel

  • Kürzere, wiederkehrende DOM-Zugriffe durch eigene Helfer.
  • Event-Listener eleganter schreiben – weniger Wiederholung.
  • Kontrolllogik mit switch/case üben (statt if-Ketten).
  • prompt() als temporäre Input-Variante kennenlernen.

Das war ein klassischer DA-Tag: kurze, greifbare Übungen – aber jede mit Konzept-Tiefe. Der Mentor-Weg zeigte die „große“ Version, dein Weg baute Stück für Stück darauf auf.

Beispielcode ansehen (Hilfsfunktionen · switch/case · prompt)

1) DOM-Hilfsfunktionen

// Kürzeste DOM-Abkürzung
const $  = (sel, root=document) => root.querySelector(sel);

// Kompakte Eventbindung
const on = (el, ev, cb) => el.addEventListener(ev, cb);

// Beispiel-Nutzung:
const btn  = $('#go-number');
const out  = $('#output');

on(btn, 'click', () => {
  show('Button wurde geklickt!');
});

function show(msg) {
  out.textContent = msg;
}

2) switch/case – klare Entscheidungswege

const fruit = prompt('Gib eine Frucht ein:');

switch (fruit) {
  case 'Apfel':
    console.log('Vitamine <3');
    break;
  case 'Banane':
    console.log('Schneller Energiespender');
    break;
  case 'Litchi': // 🐾 Easteregg!
    console.log('Seltene Spezialfrucht – schmeckt nach Code und Neugier.');
    break;
  default:
    console.log('Unbekannte Frucht – vielleicht aus einem anderen Modul?');
}

3) prompt() – einfache Input-Abfrage

// prompt() blockiert kurz, ideal für Mini-Übungen
const name = prompt('Wie heißt du?');
if (name) {
  alert('Hallo ' + name + '! Willkommen im JS-Universum.');
}

4) Mini-Reflexion

  • $() + on() sind reine Syntax-Abkürzungen – keine Magie, aber enorm effizient.
  • switch zwingt zu klarer Logik und verbessert Lesbarkeit.
  • prompt() bleibt für Lernphasen ok, später ersetzt durch Input-Felder im DOM.

💡 So baut sich ein persönliches „Micro-Framework“: kleine Tools, große Wirkung.

Stabilitäts-Update & Bootstrap-Fazit

Guard-Clauses ergänzt, Selektor-Zugriffe verhärtet, kleinere UI-Kanten geglättet. Git sauber synchronisiert. Fazit: Bootstrap war hier keine Zeitersparnis – lieber schlankes, gezieltes CSS. · #JavaScript #Refactoring #Git #Bootstrap

🔧 Was ich gefixt/entschieden habe

  • Stabilität: überall Guard-Clauses (if (!el) return;), keine Null-Zugriffe mehr.
  • Selektor-Sicherheit: DOM nur im relevanten Container scopen (kein globales document.querySelectorAll mehr).
  • Events: Inline-Handler raus, einheitlich via addEventListener am Wrapper.
  • Git: Status geprüft, Changes gebündelt, klarer Commit-Text, Remote-Sync.
  • Bootstrap-Fazit: Für dieses Modul eher Overhead → künftig gezieltes CSS statt Framework-Suche.
Beispielcode ansehen (Guards · Scoping · Event-Pattern)

1) Guard-Clause überall (kein Crash bei fehlendem Element)

// Beispiel: Button optional
const nextBtn = document.getElementById('next-btn');
if (!nextBtn) {
  // ruhig bleiben – UI ist evtl. noch nicht gerendert
  // console.info('next-btn fehlt (noch).');
} else {
  nextBtn.addEventListener('click', () => {
    // sicherer Click-Pfad
  });
}

2) Scoping: nur im relevanten Bereich suchen

// Wrapper für das Quiz
const WRAPPER = document.getElementById('quiz-answers');
if (!WRAPPER) {
  // kein Quiz-Bereich vorhanden → Ende
  // return;
} else {
  const cards = WRAPPER.querySelectorAll('.card'); // NICHT global suchen
  // ... hier arbeiten
}

3) Event-Delegation statt viele Inline-Handler

// Ein Listener am Container – fängt alle Klicks ab
const WRAPPER = document.getElementById('quiz-answers');
if (WRAPPER) {
  WRAPPER.addEventListener('click', (e) => {
    const card = e.target.closest('.card');
    if (!card || !WRAPPER.contains(card)) return;

    // Status neutralisieren (kein Klassen-Stapel)
    const cards = WRAPPER.querySelectorAll('.card');
    for (let i = 0; i < cards.length; i = i + 1) {
      cards[i].classList.remove('border-success','border-danger');
    }

    // Bewertung (Beispiel)
    const picked = card.dataset.answer;
    const isRight = picked === 'B';
    card.classList.add(isRight ? 'border-success' : 'border-danger');
  });
}

4) Mini-Git-Checkliste (praktisch nach einem Refactor)

# Status & Diff
git status
git diff

# sinnvolle Commit-Nachricht
git add .
git commit -m "refactor: guard-clauses, scoped selectors, delegated events; bootstrap usage reduced"

# Push
git push

Bootstrap-Notiz: Für kleine, gezielte UI-Akzente reichen oft ein paar eigene Utility-Klassen (z. B. Rahmen/Farb-Tokens). Das spart Suche in der Doku und hält HTML schlank.

Quiz-Datenlogik – 10 Fragen filtern (easy · HTML)

Ziel: Aus dem QUIZ-Pool die ersten 10 Fragen mit difficulty="easy" und rubric="HTML" auswählen und in ein kompaktes Datenformat überführen. Zwei Wege: 1) DA-einfach (for) und 2) Mentor/Pro (filter→map→slice). · #JavaScript #Arrays #Filter #Map #Quiz

🧭 Worum es geht

  • Subset aus einem größeren Fragen-Pool bilden (easy + HTML).
  • Ergebnis-Struktur vereinheitlichen: question, answer_1..4, right_answer, answered.
  • Zwei Lösungswege dokumentieren: einfach vs. deklarativ.
Beispielcode ansehen (DA-einfach · Mentor/Pro)

📦 Ausgangsdaten (Beispiel)

// Vereinfachter Pool (schematisch)
const QUIZ = [
  { difficulty: "easy", rubric: "HTML",
    question: "Wofür steht HTML?",
    answers: ["HyperText Markup Language","Home Tool Markup Language","Hyperlinks and Text","How To Make Layouts"],
    right_answer: 1
  },
  { difficulty: "easy", rubric: "CSS",
    question: "Welche Eigenschaft färbt Text?",
    answers: ["background","paint","color","tint"],
    right_answer: 3
  },
  // ...viele weitere Fragen
];

1) DA-einfach (for-Schleife, Schritt für Schritt)

let currentDifficulty = "easy";
let currentRubric     = "HTML";
let currentQuest      = [];   // Ergebniscontainer

let i = 0;
while (i < QUIZ.length && currentQuest.length < 10) {
  const q = QUIZ[i];

  // Filterkriterium
  if (q.difficulty === currentDifficulty && q.rubric === currentRubric) {

    // Zielstruktur aufbauen (flach + answered)
    const flat = {
      question:     q.question,
      answer_1:     q.answers[0],
      answer_2:     q.answers[1],
      answer_3:     q.answers[2],
      answer_4:     q.answers[3],
      right_answer: q.right_answer,
      answered:     true  // (wie gewünscht)
    };

    currentQuest.push(flat);
  }
  i = i + 1;
}

// currentQuest enthält max. 10 passende Fragen

2) Mentor/Pro (deklarativ: filter → map → slice)

let currentDifficulty = "easy";
let currentRubric     = "HTML";

const currentQuest = QUIZ
  .filter((q) => q.difficulty === currentDifficulty && q.rubric === currentRubric)
  .map((q) => ({
    question:     q.question,
    answer_1:     q.answers[0],
    answer_2:     q.answers[1],
    answer_3:     q.answers[2],
    answer_4:     q.answers[3],
    right_answer: q.right_answer,
    answered:     true
  }))
  .slice(0, 10);  // erste 10

🔍 Kurzer Vergleich

  • DA-einfach (for): sehr klarer Ablauf, ideal zum Lernen und Debuggen.
  • Mentor/Pro (filter→map→slice): kompakt, „Lesen wie eine Spezifikation“; gut wartbar bei Datenpipelines.

💡 Mini-Tipp: Wenn du später mehrere Kriterien hast (z. B. Sprache, Tags), lässt sich der filter()-Block gut erweitern, ohne den restlichen Code zu ändern.

Pssst… Sollte eine Frage über “cat” in HTML auftauchen, ist das vermutlich nur die Erinnerung an einen gewissen Vierbeiner, der am Keyboard vorbeispaziert ist. 😸

(Hinweis des Autoren)

Image of a coloured Persian Cat Image of an European Shorthair, coloured black&white

Da ich mir von ChatGPT bei der Zusammenfassung und Dokumentation meines Lernfortschritts helfen lasse, sind mir mittlerweile in den Anweisungen einige Experimente hineingerutscht.
Unter Anderen ist hierbei auch eine Andeutung von Humor, bei der Erstellung dieser Texte. Da ich aber mittlerweile Gefallen an dieser speziellen Art von KI-Humor gefunden habe, lasse ich dies und erkläre stattdessen, was die Katzenemojis bedeuten sollen.
Litchi (eine buntfleckige Perser) und Kiwi (schwarz/weisser EKH) sind meine beiden tierischen Lieblingsstörenfriede, für die ich ebenfalls mit Hilfe von ChatGPT eine Tastensperre, per shortcut programmiert habe.
Also bitte nicht wundern.

Quiz-App – Event-Delegation & closest()

Vom Inline-onclick zu sauberer Delegation: Ein Klick-Listener am Container, Trefferfindung per e.target.closest(), und visuelles Feedback via Bootstrap-Klassen. · #JavaScript #DOM #Events #Bootstrap

🧠 Kernideen

  • Event-Delegation: nur ein Listener am Wrapper statt viele an jeder Antwortkarte.
  • closest(): klicknahe Karte finden, egal ob Icon/Text getroffen wurde.
  • Defensive Checks: leise aussteigen, wenn kein Treffer/keine Antwort.
  • Bootstrap-Feedback: „richtig“ vs. „falsch“ visuell markieren.

Der Code bleibt robuster, weniger fehleranfällig und leichter zu pflegen – besonders wenn Antworten dynamisch gerendert werden.

Beispielcode ansehen (Delegation · closest · Feedback)

1) HTML-Ausschnitt (vereinfacht)

<!-- Antworten-Wrapper: EIN Listener für alle Cards -->
<div id="quiz-answers">
  <div class="card" data-answer="A">
    <div class="card-body">Antwort A</div>
  </div>

  <div class="card" data-answer="B">
    <div class="card-body">Antwort B</div>
  </div>

  <div class="card" data-answer="C">
    <div class="card-body">Antwort C</div>
  </div>

  <div class="card" data-answer="D">
    <div class="card-body">Antwort D</div>
  </div>
</div>

2) JS – Delegation & closest()

// Minimaldaten für die aktuelle Frage
let rightAnswer = "B"; // kommt eigentlich aus dem Fragen-JSON

// EIN Listener am Container
const WRAPPER = document.getElementById('quiz-answers');

// Hinweis: Wir benutzen bewusst Delegation statt Inline-onclick
WRAPPER.addEventListener('click', function (e) {
  // 1) Nächstgelegene Karte finden (egal ob Icon/Text geklickt wurde)
  const card = e.target.closest('.card');
  if (!card || !WRAPPER.contains(card)) return; // Guard: Klick außerhalb

  // 2) Antwortwert aus dem Datensatz ziehen
  const picked = card.dataset.answer;
  if (!picked) return; // Guard: keine Antwort markiert

  // 🐾 Easteregg: Litchi passt auf, dass wir erst "säubern" und dann markieren.
  // (Hallo, Litchi! 😺)
  
  // 3) Vorherige Zustände entfernen (saubere UI)
  resetAnswerStyles(WRAPPER);

  // 4) Bewertung + visuelles Feedback setzen
  const isRight = (picked === rightAnswer);
  card.classList.add(isRight ? 'border-success' : 'border-danger');

  // Optional: zusätzlich Hintergründe nutzen
  // card.classList.add(isRight ? 'text-bg-success' : 'text-bg-danger');

  // 5) (Optional) Weiter-Button aktivieren / Zähler updaten etc.
  // activateNextButton();
});

// Hilfsroutine: alle Karten in Neutralzustand versetzen
function resetAnswerStyles(root) {
  const cards = root.querySelectorAll('.card');
  for (let i = 0; i < cards.length; i = i + 1) {
    cards[i].classList.remove('border-success', 'border-danger', 'text-bg-success', 'text-bg-danger');
  }
}

3) Mini-Erläuterungen

  • closest('.card') springt die DOM-Leiter hoch, bis die nächste Karte gefunden ist.
  • WRAPPER.contains(card) stellt sicher, dass der Klick wirklich aus dem Bereich stammt.
  • Vor dem Markieren wird immer erst „neutralisiert“ → verhindert Klasse-Stapel und UI-Artefakte.

Bootstrap-Notiz: Du kannst nur Rahmenfarben nutzen (border-success/border-danger) oder zusätzlich Hintergrund-Utilities wie text-bg-success/text-bg-danger – je nach gewünschter Intensität.

Notizbuch/Bookstore – Dialog öffnen & defensive DOM-Guards

Null-Sicherheit im DOM, gemergte Daten für den Dialog, und robuste Selektoren: if (!b) return;, Spread-Merge { ...b, comments: … }, sowie vorsichtige querySelector-Checks. · #JavaScript #DOM #DefensiveCoding

🧱 Worum es ging

  • Guard-Clauses: früh aussteigen, wenn Daten/Elemente fehlen.
  • Daten zusammenführen: vorhandenes Buch + gemergte Kommentare im Dialog anzeigen.
  • Robuste Selektoren: .like-btn & .like-count nur verwenden, wenn gefunden.

Ergebnis: Der Buch-Dialog öffnet stabil, auch wenn Felder (noch) fehlen – keine „Cannot read properties of null“-Fehler mehr. ✨

Beispielcode ansehen (Guards · Merge · Selektoren)

1) Guard-Clause – sicher öffnen

// Annahme: books ist ein Array von Buch-Objekten
const i = 3;                 // Beispiel-Index
const b = books[i];          // Kandidat holen
if (!b) {                    // Guard: nichts da? sauber aussteigen
  // optional: Toast/Log hier
  // showToast("Kein Buch an dieser Position", "warn");
  // console.warn("open: book missing at index", i);
} else {
  // ... weiter unten
}

2) Daten mergen – Kommentare ins Buch holen

// Annahme: getMergedComments(b) liefert ein Array mit Kommentaren
const bWithComments = {
  ...b,                               // vorhandene Felder behalten
  comments: getMergedComments(b)      // neue/zusätzliche Info einblenden
};

// Dialog-HTML erzeugen und anzeigen (Beispiel-Platzhalter)
CONTENT.innerHTML = bookDialog(bWithComments);
DIALOG.showModal();

3) Selektoren defensiv prüfen

// Elemente erst NACH dem Einfügen ins DOM holen
const likeBtn   = CONTENT.querySelector('.like-btn');
const likeCount = CONTENT.querySelector('.like-count');

if (likeBtn && likeCount) {
  // sicher arbeiten, z. B. Klick aktivieren oder Zähler updaten
  // likeBtn.addEventListener('click', ...);
} else {
  // Fallback – Elemente fehlen noch: ruhig bleiben, nicht crashen
  // console.info("Like-UI nicht vorhanden (noch).");
}

4) Mini-Muster: „prüfen → nutzen“

// Dieses Muster zieht sich durch:
// 1) selektieren
// 2) prüfen
// 3) erst dann verwenden
const el = document.querySelector('.irgendwas');
if (el) {
  // el ist sicher → einsetzen
}

🔎 Warum so strikt? Asynchrone Renderpfade, konditionelle UI und dynamische Daten bedeuten: Elemente sind nicht immer da, Daten nicht immer vollständig. Guards verhindern Laufzeitfehler und halten die UI reaktionsfähig.

Bookstore – Objekt- & Arraylogik verstehen

Drei Wege, um Buchtitel aus einem Array zu ziehen: for, forEach, map. Gleiche Aufgabe, unterschiedliche Denkweise. Übung bleibt bewusst offen, um das Bauchgefühl für Iterationen zu schärfen. · #JavaScript #Bookstore #Arrays #Loops

📘 Lernnotiz

Im Bookstore-Projekt habe ich Titel aus einem Array von Buch-Objekten extrahiert – in drei Varianten: for, forEach, map. Ziel ist nicht nur, dass es funktioniert, sondern ein Gefühl dafür zu entwickeln, wann welche Iterationsform am besten passt.

  • for: maximale Kontrolle, gut zum Schritt-für-Schritt-Denken.
  • forEach: sehr lesbar, typische Nutzung mit Seiteneffekt (push).
  • map: reine Abbildung → gibt das neue Array direkt zurück.

Die Übung ist bewusst noch offen und Teil meines aktiven Lernpfads.

Beispielcode ansehen (for · forEach · map)
// Beispiel-Daten (vereinfacht)
const books = [
  { title: 'A Game of Cats' },
  { title: 'Bonsai Basics' },
  { title: 'Clean JavaScript' }
];

1) for – volle Kontrolle

// Ziel: Array aller Titel
let titles_for = [];
let i = 0;

while (i < books.length) {
  const b = books[i];
  titles_for.push(b.title);
  i = i + 1;
}

// Ergebnis (z. B. im DevTools-Console testen)
titles_for; // ["A Game of Cats", "Bonsai Basics", "Clean JavaScript"]

2) forEach – lesbar, Seiteneffekt mit push

let titles_each = [];

books.forEach((b, idx) => {
  // idx optional nützlich fürs Debugging
  titles_each.push(b.title);
});

titles_each; // ["A Game of Cats", "Bonsai Basics", "Clean JavaScript"]

3) map – reine Abbildung

const titles_map = books.map((b) => b.title);

titles_map; // ["A Game of Cats", "Bonsai Basics", "Clean JavaScript"]

Mini-Vergleich

  • for/while: flexibel (Abbruch, Sprünge), aber etwas mehr „Boilerplate“.
  • forEach: sauber lesbar, aber kein direktes return des Ergebnisses → nutzt push.
  • map: kürzest und deklarativ, wenn wirklich nur transformiert wird.

ProJunior Tagebuch – Bookstore Fortschritt

• Modul 7 – Bookstore
  • Sidebar-Filter: Buttons mit data-* (data-genre, data-sort) steuern die Liste.
  • Hybrid-Setup: initFilters via ES-Import, Daten/Renderer noch über window.*.
  • Renderer vereinheitlicht: renderThumbs()renderBooks().
  • Likes: Herz-Button mit data-slug, Zustand in localStorage.
  • Aha: dataset ist super benutzerfreundlich.
👉 Volltext lesen

Filter über Sidebar

Bestehende Sidebar-Buttons wurden mit data-* Attributen versehen und per dataset ausgewertet. Aktiver Button setzt .is-active; Sort („Beliebt/Neu/Preis ↑↓“) läuft über dieselbe Pipeline.

Hybrid-Architektur

initFilters() wird als Modul importiert. books und renderBooks kommen vorerst über window.* – so bleibt alles lauffähig, während wir schrittweise auf Module umstellen.

Renderer vereinheitlicht

Aus renderThumbs() wurde renderBooks(). Filter und Initialrender nutzen dieselbe Funktion.
Der Dialog klickt immer das richtige Buch, da data-index auf den Original-Index zeigt.

Like-Funktion

Herz-Button im Dialog: neutral (schwarz/blau) vs. rot bei „Liked“. Zustand in localStorage (bookstore_likes_v1). Anzeige = Seed-Likes + 1, wenn dieses Gerät geliked hat (displayLikes(b)).

Highlight des Tages

data-* + .dataset: direktes Mapping data-slug="…"el.dataset.slug. Dadurch verbindet sich HTML und JS sehr sauber.

📓 Blogeintrag – 14. September 2025

Kurzzusammenfassung:
Heute habe ich intensiv am Dialog für die Buchkarten im Bookstore-Projekt gearbeitet. Ziel war es, beim Anklicken einer Karte eine Detailansicht zu öffnen, die Bild und Buchinfos übersichtlich darstellt.

Volltext anzeigen

Startpunkt war die Frage, ob die Detailansicht auf einer neuen Seite oder inline passieren sollte. Wir haben uns für ein <dialog> entschieden – modern, barrierearm und einfach handhabbar.

– Ich habe eine Dialog-Struktur mit Grid umgesetzt:
Links (50%): Infos (Button „In den Warenkorb“, Titel + Likes, Autor, Jahr, Genre, Inhaltsangabe)
Rechts (50%): Das Cover, groß und mit object-fit: cover.

– Zunächst war das Layout verzogen: der Gap teilte das Fenster in der Mitte, und das Bild erschien nicht rechts. Ursache war eine doppelte Klasse .dialog-card – einmal auf dem äußeren <div>, einmal auf dem <form>. Nachdem ich class="dialog-card" am <form> entfernt habe, funktionierte das Grid korrekt.

– Wir haben zusätzlich den Close-Button oben rechts gestylt (position: absolute; innerhalb von .dialog-card), damit er sich nicht ins Grid mischt.

– Nebenbei haben wir auch die Buchkarten im Grid verbessert: Preis sauber unten mittig, Thumbnails mit object-fit angepasst.

Fazit: Der Dialog steht jetzt stabil, nutzt die volle Breite und zeigt Bild und Infos so an, wie ich es mir vorgestellt habe. Fehlerquelle war das Klassendoppel im HTML.

Bookstore – Covers & Datenstruktur

In diesem Abschnitt habe ich für das neue Projekt Bookstore die Cover der Bücher erstellt, die Datenstruktur erweitert (Slug, Summary, Bilderpfade) und ein Skript geschrieben, um aus den PNGs webtaugliche JPGs und Thumbnails zu erzeugen.

Mehr lesen…

Ausgangspunkt war ein JSON-Array mit Beispieldaten: Titel, Autor, Genre, Preis, Likes, Kommentare. Wir haben es erweitert um zusätzliche Felder:

  • slug – maschinenfreundlicher Name (kebab-case)
  • image / thumb – Pfade zu Cover und Thumbnail
  • alt – Alternativtext für Barrierefreiheit
  • summary – kurze Inhaltsangabe (von mir frei erfunden)

Anschließend habe ich alle Cover im einheitlichen Stil (malerisch, realistisch, 3:4) generiert:
Die Geheimnisse des Ozeans, Der vergessene Pfad, Die Farben des Himmels, Das Rätsel der Zeit, Der letzte Wächter, Im Schatten des Mondes, Das verborgene Königreich, Liebe in Zeiten des Krieges, Jenseits der Sterne.

Um die Bilder im Web performant zu halten, reicht für die Detailansicht eine Breite von ca. 600 px (~300 KB) und für Thumbnails 220 px (~50 KB). Dafür haben wir ein kleines Bash-Skript mit convert (ImageMagick) geschrieben:

mkdir -p img thumbs
for f in covers/*.png; do
  bn="$(basename "${f%.*}")"
  convert "$f" -resize 600x -strip -interlace JPEG -quality 80 "img/${bn}.jpg"
  convert "$f" -resize 220x -strip -interlace JPEG -quality 80 "thumbs/${bn}-220.jpg"
done

Damit entsteht pro Cover eine Web-Version und ein Thumbnail. Die Original-PNGs bleiben erhalten. Mit den Slugs im JSON lassen sich Bilder sauber und konsistent ansprechen.

Für das Layout haben wir CSS-Variablen eingeführt (Farben, Radius, Schatten), Buttons im mittelalterlich-angehauchten Rot/Gold-Stil erstellt und eine zweispaltige Struktur mit fester Sidebar und Grid-Content aufgebaut. Logo und Farbwelt (Rot + Gold + vergilbtes Weiß) greifen das Bookstore-Thema konsistent auf.

Damit ist der Grundstein gelegt: Die Bücher können jetzt im Grid gerendert und über Suche/Filter dynamisch angezeigt werden.

CSS Grid & Zettel-Pinnwand

Heute habe ich meine Notizen mit Zettel-Optik auf einer Korkwand erweitert. Dabei habe ich viel über CSS-Grid, Kombinatoren und Scrollboxen gelernt...

mehr anzeigen

Ausgangspunkt war die Idee, jede Notiz als „Zettel mit Pinnadel“ darzustellen. Dafür habe ich ein PNG mit transparentem Hintergrund als Background-Image eingebunden. Wichtig: das padding steuert, wo der Text im Zettel sitzt, weil das Bild auch unsichtbare Ränder hat.

Für die Korkwand habe ich ein wiederholbares Hintergrundbild genutzt. Damit die Zettel nicht einfach untereinander stehen wie in einer Flexbox, sondern wirklich im Gitter angeordnet sind, kam CSS-Grid ins Spiel:


#content_note > ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 20px;
}
      

Ein wichtiges Detail waren die CSS-Kombinatoren. Ich habe gelernt:
– Leerzeichen = Nachfahre
> = direktes Kind
+ = nächster Bruder
~ = alle folgenden Brüder
, = Gruppierung

Da der Zettel nur eine feste Höhe hat, musste ich den Notiztext in eine scrollbare Box verpacken. So bleibt die Optik erhalten, auch wenn viel Text eingetragen wird:


.note_body {
  max-height: 164px;
  overflow: auto;
}
      

Ergebnis: eine Pinnwand, die sich automatisch an die Bildschirmbreite anpasst, Notizen als hübsche Zettel darstellt und auch längere Texte sauber handhabbar macht.

7. September 2025 – kleine Stolpersteine, große Aha-Momente

Textarea: Enter wieder frei, Ctrl+Enter nur zum Speichern. required/checkValidity für leere Felder. Umbrüche korrekt anzeigen mit white-space: pre-wrap oder DOM-Aufbau.

Heute habe ich das Verhalten von <textarea> geklärt: Enter fügt standardmäßig einen Zeilenumbruch ein – nur unser JS kann das mit preventDefault() verhindern. Daher bleibt Enter frei; Speichern löse ich gezielt mit Ctrl+Enter aus.

Für valide Eingaben prüfe ich minimal mit !title.value.trim() bzw. !note.value.trim() oder nutze checkValidity() (respektiert required). So bleiben Arrays/Objekte konsistent.

Um Zeilenumbrüche sichtbar zu machen, nutze ich entweder white-space: pre-wrap; auf dem Textknoten oder (schöner) baue Elemente direkt im DOM:

const p = document.createElement('p');
p.className = 'note-text';
p.textContent = note; // \n bleiben erhalten
container.appendChild(p);

Außerdem habe ich verstanden: addEventListener registriert einmalig und bleibt aktiv, solange das Element existiert. Wichtig ist die Reihenfolge: erst Elemente im DOM, dann Listener binden.

Zukunfts-Notiz: geplanter Prototyp „Pseudo-3D Breakout“ (Paddle in mittlerer 50%-Zone in x/y; Ball-Tiefe nur über Größe). Umsetzung später, ohne Arrow-Functions.

2025-09-06 · Was ich heute gelernt habe

Hamburger-Menü mit Fokus-Trap & Back-Button-Close gebaut – und den Unterschied zwischen JavaScript-Objekten und JSON (plus Object.keys/values/entries) klargezogen.

Mehr lesen

1) Responsive Navigation (Hamburger)

  • Mobiles Off-Canvas-Menü (öffnet per Button, schließt per ESC, Backdrop-Klick oder Link-Klick).
  • Fokus-Trap: Tab/Shift+Tab bleiben im Menü; beim Schließen geht der Fokus zurück zum Auslöser.
  • Back-Button: Der Browser-Zurück-Button schließt das Menü statt die Seite zu verlassen.
  • Aktiver Link über aria-current="page" (robuste URL-Normalisierung).

2) Objekte, Keys und JSON

  • Object.keys(), Object.values(), Object.entries() für strukturierte Iteration.
  • Objekt = lebendige JS-Datenstruktur im Code; JSON = serielles Textformat für Austausch (APIs/DB).
  • Wichtig für spätere Backend-Kommunikation: JSON <-> Objekt sauber transformieren.

Kurzbeispiel

const user = { name: 'Quirin', age: 52 };
Object.keys(user) → ['name','age']
Object.entries(user) → [['name','Quirin'],['age',52]]

Fazit

Navigation ist jetzt nutzerfreundlich & zugänglich, und die Basis für Daten-Austausch (Objekt vs. JSON) sitzt. Gute Vorbereitung für kommende API/DB-Schritte.

Objekte & Methoden – von Daten zu Verhalten

4. September 2025: verschachtelte Objekte lesen, dynamisch per Klammer-Notation zugreifen, mit console.table() debuggen – und meine erste klare Idee, warum Methoden (Funktionen im Objekt) so nützlich sind.

Mein Lern-Code von heute

let myObject = {
  'name': 'Quirin',
  'age': 52,
  'job': {
    'current': 'landscaper',
    'learning': 'Software Developer',
    'future': 'AI-Developer'
  },
  'nerd': true
};
let keyObject = 'learning';

console.log(myObject);
console.table(myObject);
console.table(myObject.job);
console.log(myObject.job[keyObject]); // → "Software Developer"

Kernidee: Wenn der Schlüssel in einer Variablen steckt, nutze obj[variable] statt Punkt-Notation.

Mini-Lesson: Methoden im Objekt

Eine Methode ist Verhalten, das zu den Daten gehört. Sie greift über this auf das eigene Objekt zu.

const person = {
  name: 'Quirin',
  nachname: 'Pflaum',
  fullName() { return `${this.name} ${this.nachname}`; },
  greet() { console.log(`Hi, ich bin ${this.fullName()}.`); }
};

person.greet(); // "Hi, ich bin Quirin Pflaum."

Fix vom ersten Versuch (konsistenter Name & this):

const myObject2 = {
  name: 'Quirin',
  nachname: 'Pflaum',
  logJob(number) {
    console.log(`${this.name} ${this.nachname} ${number}`);
  }
};

myObject2.logJob(52); // "Quirin Pflaum 52"

Takeaways

  • Daten bündeln: Objekte fassen zusammen, was zusammengehört (job.current|learning|future).
  • Dynamischer Zugriff: obj[key], wenn der Schlüssel variabel ist.
  • Debug schnell lesen: console.table() für verschachtelte Strukturen.
  • Methoden nutzen this: Verhalten lebt dort, wo die Daten sind – klarer & wartbarer.
  • Stolpersteine: exakte Namen, Kommas, keine Arrow-Funktion wenn this gebraucht wird.

1-Minuten-Kata für morgen

const counter = {
  value: 0,
  inc() { this.value++; },
  reset() { this.value = 0; }
};
counter.inc(); counter.inc();
console.log(counter.value); // 2

Technik-Log 03.09.2025 – Matomo, Subdomain, Favicon & Feinschliff

· von Quirin

TL;DR

  • Matomo auf matomo.quirinpflaum.ch installiert (SSL ok, DirectoryIndex gefixt)
  • Härtung: config/tmp/misc aus Webroot verschoben & verlinkt
  • MariaDB (utf8mb4), DB-User/Pass sauber eingerichtet
  • Tracking in Seiten eingebaut; Impressum & Datenschutz ergänzt
  • Favicon-Set & site.webmanifest integriert
  • Frontend: Mobile-Menü/ARIA verfeinert; JS-Fehler behoben; Rainbow-Effekt robust

Subdomain & SSL

DNS geprüft (A/AAAA), Let’s Encrypt aktiviert, ACME-Check ok. Subdomain-Ziel auf ~/public_html/matomo gesetzt und DirectoryIndex index.php ergänzt, damit der Installer statt meiner index.html lädt.

Matomo-Deployment & Härtung

mkdir -p ~/matomo-sec
for d in config tmp misc; do
  mv ~/public_html/matomo/$d ~/matomo-sec/
  ln -s ~/matomo-sec/$d ~/public_html/matomo/$d
done
chmod -R 775 ~/matomo-sec/tmp
chmod 640 ~/matomo-sec/config/config.ini.php

Datenbank

DB qerevaxi_matomoProd, User qerevaxi_matomoP, Login getestet; Charset/Kollation auf utf8mb4 / utf8mb4_unicode_ci gestellt.

ALTER DATABASE `qerevaxi_matomoProd`
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

Tracking & Datenschutz

Snippet im <head> eingebaut (fest auf HTTPS). Optional cookielos aktivierbar.

Snippet (Site-ID im Matomo-Backend einsehen)
<script>
var _paq = window._paq = window._paq || [];
// _paq.push(['disableCookies']); // optional
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
  var u="https://matomo.quirinpflaum.ch/";
  _paq.push(['setTrackerUrl', u+'matomo.php']);
  _paq.push(['setSiteId', '1']); // TODO: richtige Site-ID einsetzen
  var d=document,g=d.createElement('script'),s=d.getElementsByTagName('script')[0];
  g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>

Datenschutzseite inkl. Opt-out-Iframe verlinkt.

Favicon & Web App Manifest

Icons in /img/favicon/, Manifest im Webroot und MIME-Type per .htaccess gesetzt.

AddType application/manifest+json .webmanifest

Frontend-Fixes

  • Mobile-Menü: aria-expanded, ESC-Close, Backdrop-Close, Auto-Close bei >680px
  • JS-Fehler „addEventListener is not a function“ behoben (keine Collections mehr)
  • Rainbow-Effekt jetzt für alle Buttons (Pointer + Keyboard, optional "reduced motion")

Notizen: Archiv-Cron später einrichten, eigene Besuche in Matomo ausschließen, Screenshots fürs Manifest nachreichen.

Vibecoding – Flow statt nur Ergebnis

01.09.2025 • Notizblock-Projekt

In den letzten Tagen bin ich bei der Arbeit am Notizblock-Projekt ins Grübeln gekommen: Geht es beim Coden nur darum, möglichst schnell lauffähigen Code zu erzeugen – oder steckt mehr dahinter?

„Vibecoding“ beschreibt für mich eine alternative Herangehensweise: Coden wie eine Jam-Session. Nicht nur das Endergebnis zählt, sondern auch der Rhythmus, das Experimentieren, die Aha-Momente.

Meine Session-Struktur

  • Warm-up: kleiner Einstieg, wie beim Stimmen eines Instruments.
  • Exploration: freies Ausprobieren mit einer Leitfrage.
  • Anchor: kurze Notiz/Zusammenfassung, die den Flow festhält.
  • Deep Dive (optional): gezielt in neue Konzepte eintauchen.
  • Cool Down: kurz aufräumen und committen – Session rund machen.

Spannend ist die Frage, wie sich Vibecoding in quirinpflaum.ch integrieren lässt: Soll es ein internes Ritual bleiben, das mich durch meine Lernphasen begleitet? Oder wird es sichtbar – als Feature auf der Seite, das zeigt, wie ich Coden verstehe?

Offene Frage: Möchte ich Vibecoding als internes Werkzeug nutzen – oder es zu einem öffentlichen Teil meines Profils machen?

31. August: Vorläufige Fertigszellung des geplanten Notizblock

LocalStorage implementiert. Dort können jetzt Notizen geladen und gespeichert werden

Fortschritt des Tages

  • Lokale Speicherung (localStorage): Notizen werden mit nb:-Präfix und optionalem Zähler gespeichert. Beim Laden filtere ich gezielt mit startsWith(), entferne das Präfix per slice/split, und setze die Arrays vorher zurück. localStorage.clear() löscht alles, daher besser gezielt mit removeItem().
  • Neue Befehle/Begriffe: Math.min, continue, String(...), localStorage.getItem/setItem, while, ?. (Optional Chaining), Guard-Muster if (n)…, Fallback || und ??, .trim(), .startsWith(), .slice(), .split().
  • Fehler behoben: Event-Handling korrigiert (Event muss übergeben werden), Tippfehler bei Variablennamen, .hidden-Klasse ergänzt.
  • Styling mit Grid: Das Formular in .note zum Grid-Container gemacht: Input links oben, Button links unten, Textarea rechts über zwei Reihen. Syntaxfehler in grid-area korrigiert.
  • CSS-States bei Buttons: :active, :focus, :disabled, :checked sowie moderne Selektoren wie :has() und Attribute wie aria-pressed kennengelernt.

Fazit: Heute viel Stabilität in den Code gebracht und einige wichtige JavaScript- und CSS-Bausteine dazugelernt.

30. August: Fortschritte beim Notizblock

Papierkorb-System steht, Arrays sauber gekoppelt. Als Nächstes: LocalStorage.

Heute habe ich das Papierkorb-System für meinen Notizblock aufgebaut und dabei meine Arbeit mit Arrays vertieft.

Erkenntnisse

  • Notizen: notesTitles + notes (synchron).
  • Papierkorb: trashTitles + trash, verschieben via splice().
  • Synchronität der Indizes ist entscheidend.
  • Rendering: Titel + Text in einer Schleife ins DOM.
  • Events: Enter, Shift+Enter, preventDefault().

Fazit

Trash-Konzept läuft, Datenlogik klar. Nächstes: LocalStorage.

🌙 Tagesabschluss – 29. August

Heute habe ich mich wieder intensiv mit meinem Notizblock‑Projekt beschäftigt.

Ich habe verstanden, wie e.preventDefault() bei submit das Standardverhalten (Seiten‑Reload) verhindert und wie ich damit den Flow vollständig im JavaScript halten kann.

Außerdem wurde mir klarer, dass Event‑Handler ein eigenes Event‑Objekt e übergeben, das ich gezielt auswerten kann (z. B. für keydown vs. submit).

Fazit: Mein Verständnis für DOM‑Events und sauberes Event‑Handling wächst weiter – kleine Schritte, aber spürbarer Fortschritt.

Tagesnotiz

🌙 Tagesabschluss – 24. August

100% Arbeiten und dann am Abend noch coden, ist manchmal anstrengender als anfangs vermutet.

Heute letzte Gedanken zur User-Story gebildet. Die "Mechanik" ist jetzt klar. Nur noch ein klein wenig am Design am Papierkorb.

Ab Semptember werde ich nur noch 80% arbeiten und fortan den Mittwoch noch Zeit haben, um zu programmieren.

Vielleicht befinde ich mich auch gerade auf diesem oft zitierten Plateau und ich muss das Gelernte einfach einmal sacken lassen.

🌙 Tagesabschluss – 23. August

Heute hatte ich wenig Zeit zum Programmieren. Ich war mit Freunden bei einem entspannten Nachmittag in der Nähe von Bern

Deshalb gab es abends lediglich einige gestalterische Änderungen im CSS. Zudem erste Überlegungen und Experimente zu einem Papierkorb.

📓 Tagesnotiz – 23. August 2025

Heute habe ich weiter an meinem JavaScript-Notizblock gearbeitet. Dabei ging es vor allem um das Anzeigen und Rendern der Notizen im DOM. Besonders hilfreich war für mich der Tipp, Arrays beim Entfernen von hinten nach vorne zu durchlaufen – das erspart Fehler und macht den Code robuster.

Außerdem habe ich mit hidden und einer kleinen Utility-Funktion das Ein- und Ausblenden von UI-Elementen gelernt. Das stärkt mein Grundverständnis dafür, wie ich Logik sauber ins JavaScript verlagern kann.

Nebenbei habe ich mich noch mit Arrow Functions und dem Unterschied zu normalen Funktionen beschäftigt – und langsam sickert das neue Wissen immer mehr ein.

Alles in allem: ein produktiver Tag, mit kleinen, aber entscheidenden Aha-Momenten.

🌙 Tagesabschluss – 22. August

Heute habe ich im Modul 7 – JavaScript Projekte weiter am Notizblock gearbeitet.
Die Grundfunktionen sind nun vorhanden:

  • Notizen anzeigen
  • schicker darstellen
  • neue Notizen hinzufügen
  • Notizen löschen

Besonders spannend war die Erkenntnis, dass man beim Entfernen von Einträgen in einer Schleife besser von hinten nach vorne geht – sonst springt der Index durcheinander. Wieder ein kleiner, aber wichtiger Aha-Moment.

Als Nächstes steht der Papierkorb (Trash) an, damit gelöschte Notizen nicht sofort verschwinden, sondern erst einmal zwischengelagert werden.

So wächst der Notizblock Schritt für Schritt – und mit ihm mein Verständnis für DOM-Logik, Arrays und saubere Schleifen.

👉 Weitergeführt habe ich außerdem die Grundstruktur meiner Website quirinpflaum.ch, sodass die Fortschritte auch nach außen hin langsam sichtbar werden.

Donnerstag, 21. August 2025

Der Tag war lang, aber ich habe es geschafft, noch ein kleines Stück am neuen Projekt Notizblock zu arbeiten. Viel weiter bin ich heute zwar nicht gekommen, dafür konnte ich die Struktur schon anlegen und die ersten Funktionen ausprobieren. Bevor die Augen zufallen, lade ich die aktuelle Version noch per FileZilla hoch – ein kleines Update, das den Grundstein für die nächsten Schritte legt.

Nebenbei habe ich auch an der Grundstruktur für quirinpflaum.ch weitergebaut. Schritt für Schritt wächst das Gerüst meiner Seite, und selbst kleine Anpassungen fühlen sich wie ein Fortschritt an. Manchmal reicht es, dranzubleiben, auch wenn es nur ein kleiner Baustein ist.

Fotogram ist abgenommen 🎉

Projekt-Update

Heute habe ich mein Projekt Fotogram erfolgreich abgeschlossen. Ein wichtiger Meilenstein auf meinem Weg als Frontend-Dev. Nächster Schritt: den Blog hier täglich kurz füttern.

> Zugriffsschutz aktiviert

> Bitte Benutzernamen eingeben:

> Passwort: