Compare commits

..
78 Commits
Author SHA1 Message Date
libretechandClaude Opus 4.8 9a4b4536cd 223015b+c klausurfragen: stand 03.07
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 23:03:26 +02:00
libretechandClaude Opus 4.8 102a6d53e5 223015b+c klausurfragen: sose26-klausurset kuratiert + wording-fixes
- 223015c: G7 id-selektor neu (ersetzt spezifität-ansatz), B10 encapsulation
  + E3 gesamtablauf disabled (tcp-handshake nie ausreichend behandelt),
  G6 ohne hsl + farbname/color code, H6 curb-cut-wording (genau zwei
  beispiele, klickflächen/kontrast/tab+enter), H7 attribute statt tags
  + screenreader-distraktor + tastatur-wording, B7b dns: domain->ip
- 223015b: J22 ohne buch-lesen + überwiegend unabhängig, K2 sie-form
  + taschenrechner-hinweis, strg/cmd+f
- .gitignore: slides/**/*-INTERN.md (lokale klausur-previews, nie deployen)
- 223015b/CLAUDE.md: deck-doku + downstream-verweis aufs klausur-repo

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 13:37:01 +02:00
libretech 983cdea235 223015b klausurfragen: disabled-fragen + j6/j7/j8/j16 entfernt, j14a (farbwerte pro byte) ergänzt, ecc-zeile raus, j22-frage layout entzerrt 2026-07-02 19:23:26 +02:00
libretech 2d926f153e 223015c klausurfragen: alle disabled-fragen entfernt, kuratierung reduziert 2026-07-02 19:23:26 +02:00
libretech a1381dc717 223015c: kapitel 3 (javascript) aus deploy-kapiteln entfernt 2026-07-02 19:23:25 +02:00
libretech c7045d96e4 223015b+c klausurfragen: frage-folie ohne lösung je aktiver frage vorangestellt, bestehende als (lösung) markiert; sortiere->sortieren sie 2026-06-24 13:39:01 +02:00
libretech b500e2f647 223015b klausurfragen: auf sie umgestellt + emojis raus, fragen-set kuratiert (j/l/m/n/o aktiviert bzw. ausgegraut), l1 auf vorwiegend verlustbehaftet präzisiert, bandbreite-fragen ergänzt, stand 24.06 2026-06-24 13:28:10 +02:00
libretech fdd4bbaa54 223015c klausurfragen: client-server-modell + ip/mac/port/dns-glossar ergänzt, stand 24.06 2026-06-24 13:14:56 +02:00
libretech b8ca038153 223015c klausurfragen: nicht-prüfungsrelevante fragen ausgegraut 2026-06-24 13:01:23 +02:00
libretech c7811f4001 index: 223015b klausurfragen freigeschaltet 2026-06-17 14:27:13 +02:00
libretech 3ae2f6881a index: klausurfolien/klausurfragen-karten per-kurs schaltbar; 223015b klausurfolien freigeschaltet, 223015c klausurfragen 2026-06-17 14:13:15 +02:00
libretech a13440b519 223015c: klausurfragen auf sie vereinheitlicht + 6 fragen ergänzt (a5/c11/c12/f4/g5/g6), emojis raus, stand aktualisiert; index: kapitel 03 + klausurfragen freigeschaltet 2026-06-17 13:57:20 +02:00
libretech 800a7d6ff9 223015c: farben-overflow gefixt (intro gekürzt, code kompakt), klausurfolien regeneriert 2026-06-17 13:34:55 +02:00
libretech d01253d0d0 223015c 02: anwendungsschicht benannt + an abstraktion angeknüpft (browser/app -> anwendung) 2026-06-17 13:01:00 +02:00
libretech 7885aee8fc 223015c 02: schicht-nummern auf 4-schichten-modell vereinheitlicht, alle abkürzungen bei erstnennung aufgelöst 2026-06-17 12:17:55 +02:00
libretech 27199ee1cc 223015c 03: hands-on-imperative auf ihr-plural vereinheitlicht 2026-06-17 12:17:55 +02:00
libretech 1dd983a453 223015c 01: tippfehler/grammatik/fachfehler korrigiert, frage-headings entschärft 2026-06-17 12:17:55 +02:00
libretech 4cd9975017 223015c 00: frage-headings/sie-anrede/drama entschärft 2026-06-17 12:17:55 +02:00
libretech a64382d2de 223015c 01: byte/bit ohne plural-s 2026-06-17 12:03:40 +02:00
libretech 45c554c875 223015c 02: alltagsmetaphern entschlackt, vertiefung sortiert, dns-hierarchie präzisiert, byte/bit ohne plural-s 2026-06-17 12:03:39 +02:00
libretech 841d8b5944 223015c demos: port-grafik baseline-fix, handshake-caption vervollständigt, dingbat raus 2026-06-17 12:03:38 +02:00
libretech 613f37983e 223015c 02: osi/tcp-ip-grafik korrekt gemappt + headline-umbruch fix 2026-06-17 10:39:08 +02:00
libretech 2658251bcb 223015c 03: datum/emoji raus, frage-headings entschärft, du-konsistenz 2026-06-17 10:36:16 +02:00
libretech 3d6059c070 223015c 02: deutsch-qualität, fragmente/anglizismen raus, neutraler ton 2026-06-17 10:36:16 +02:00
libretech ed756461e6 223015c 02: anrede durchgängig duzen, emoji raus, http/2.0 vereinheitlicht 2026-06-17 09:59:58 +02:00
libretech 028f5f0fb8 223015b kap2: demos + barn rgb/cmyk
- 18 demos aus refactor/didaktik-rewrite-2026 cherry-picked (aliasing-paar, byte-fuer-byte, container-vs-codec, drei-dateien, jpeg-pipeline, kap02-05-eroeffner, kb-vs-kib, nyquist-diagram, pivot-signal-zu-byte, psychoakustik/psychovisualitaet-grafik, raster-vs-vektor, summary-kap02, summary-organizer, usb-c-drei-achsen)
- barn-rgb.png + barn-cmyk.png neu generiert (analog zu ybcbr.webp, kanaltrennung mit korrekter k-extraktion)
- lv-original-vs-fake.jpg + 02-bild-audio-video.md slides wired up
2026-06-11 09:55:58 +02:00
libretech 7ae2a9964a 223015c 02: layer 1 (bit) ergänzt, tabellen-refactor, http/2.0, dimmed/klausur-klassen 2026-05-22 09:47:58 +02:00
libretech abc8dece9c 223015b 01: edit-müll raus (style-tag-injection + eof-tippfehler) 2026-05-15 22:06:58 +02:00
libretech a5f890ae24 hdm fixes 2026-05-15 09:44:50 +02:00
libretech 56901d6068 update logos 2026-05-14 22:58:35 +02:00
libretech 2184870e0b dhbw 01-intro: über-mich collage refresh + wording-fixes + index-script polish
- collage.png neu: 11 marken (3 spalten brick-staggered + breite kombi-row für klett+pons+langenscheidt), normalisiert mit padding rechts, dhbw-dark + uni-stuttgart-dark variants (light-svgs unsichtbar auf weiß)
- neue logos: pons.svg (wikimedia, ersetzt gecropptes pons.jpg), bosch.svg, leibniz-fh.svg (alle wikimedia), hdm.jpg, libretech.png (gitea avatar), nextlevelshit.png (github avatar), pons-langenscheidt-klett.png (kombi)
- 01-intro slide: "Web App" -> "Web Applikation" (PWA-fachbegriff bleibt), "Betriebssystem (GNU/Linux, Windows, macOS, iOS, Android)" als erste kategorie ergänzt + OSs aus desktop/mobile rausgezogen, "ODER" -> "XOR"
- scripts/generate-index: dhbw spezifisch — KONTAKT_URL/LABEL pro course, QR_IMG-pfad pro course, footer 'Kursübersicht'-link conditional auf BREADCRUMB_URL, dhbw-subtitle ohne hdm-modul-name
2026-05-09 20:47:03 +02:00
libretech 755aadbc6a scripts/generate-index: per-course BREADCRUMB_URL + EXAM_URL/EXAM_LABEL — dhbw ohne 'kursübersicht'-link (ging fälschlich auf homepage), dhbw mit prüfungsleistung-link (git.librete.ch/DHBW/pruefungsleistung) 2026-05-08 21:49:55 +02:00
libretech 3554908246 scripts/generate-index: dhbw-case ergänzt (web engineering, dhbw stuttgart, himbeer-akzent, sitzung statt kapitel), HEADLINE+INSTITUTION+KAPITEL_TERM pro course, dhbw-topics in TOPIC_MAP (html-css, js-frameworks, nodejs-basics, express, testing, typescript, docker, best-practices) 2026-05-08 21:39:55 +02:00
libretech 2ac3303167 dhbw: file-namen auf dash-convention vereinheitlicht (consistent mit hdm), Makefile dhbw_KAPITEL aktualisiert 2026-05-08 21:36:59 +02:00
libretech 7970ba84d1 dhbw 09_best_practices: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:57:44 +02:00
libretech bb7c2730ca dhbw 08_docker: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:57:14 +02:00
libretech 4f4e989721 dhbw 07_typescript: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:56:42 +02:00
libretech 32a633188a dhbw 06_testing: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:56:09 +02:00
libretech 8ae050baad dhbw 05_express: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:55:33 +02:00
libretech 19d05252ef dhbw 04_nodejs_basics: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:54:53 +02:00
libretech e1c75a1ba3 dhbw 03_js_frameworks: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:54:17 +02:00
libretech c21a073984 dhbw 02_html_css: qr-code-folie nach cover + url unter cover, mirror hdm-pattern 2026-05-08 13:53:36 +02:00
libretech bd473fc890 dhbw 01_intro: qr-code-folie nach cover (zeigt auf librete.ch/dhbw/) + url unter cover, mirror hdm-pattern 2026-05-08 13:52:40 +02:00
libretech 95ff4a08d7 dhbw assets: qr-code zu librete.ch/dhbw/ (slides-dhbw.png) für post-cover-folien 2026-05-08 13:51:54 +02:00
libretech 29403e0f78 dhbw 01_intro: was-keine-webapp-folie auf * statt - bullets für marp-fragmented-list 2026-05-08 13:35:44 +02:00
libretech 0a7730ad9a dhbw 01_intro: vorstellungsrunde-folie zwischen über-mich und vorlesungsplan (gap aus original-PDF slide 4 — wer bist du, was machst du) 2026-05-08 13:32:43 +02:00
libretech c4c6119b8a dhbw 01_intro: was-ist-keine-webapp-block (cover + content) zwischen prüfungsleistung-abgabe und internet-timeline (gap aus original-PDF slides 8/9) 2026-05-08 13:30:43 +02:00
libretech a830480100 dhbw 01_intro: erwartungen-und-wünsche-folie zwischen vorlesungsplan und prüfungsleistung (gap aus original-PDF slide 6) 2026-05-08 13:27:46 +02:00
libretech a56b487bd4 dhbw 01_intro: editor-folie titel präzisiert (Editor → Editor (IDE)) 2026-05-08 13:24:27 +02:00
libretech 81f233b61a dhbw slides: 1 file pro sitzung (issue #2)
01_web_eng.md (103 slides) aufgesplittet:
- 01_intro.md: cover, ueber-mich, vorlesungsplan, pruefungsleistung, internet 101, onboarding
- 02_html_css.md: html, a11y/wcag, css basics + css extended (aus 02_css_extended.md gemergt)
- 03_js_frameworks.md: ecmascript, js, dom, fetch, async, frameworks, build tools, microfrontends

renames (03-08 -> 04-09):
- 03_nodejs_basics -> 04_nodejs_basics
- 04_nodejs_advanced -> 05_express
- 05_testing -> 06_testing
- 06_typescript -> 07_typescript
- 07_docker -> 08_docker
- 08_best_practices -> 09_best_practices

02_css_extended.md geloescht (inhalte in 02_html_css.md)
makefile: dhbw_KAPITEL + dhbw_NAME aktualisiert
2026-05-08 09:06:19 +02:00
libretech 0774b8652d dhbw 01: pruefungsleistung aktualisiert (100 basis + 15 bonus statt 75+10)
punkteverteilung nach source-of-truth (git.librete.ch/DHBW/pruefungsleistung):
- grundanforderungen 30p (projektgrundlagen 15 + praesentation 15)
- clean code 25p (struktur 10 + qualitaet 10 + doku 5)
- accessibility & ux 25p (a11y 15 + responsive 10) — neu
- technische umsetzung 20p — neu
- bonus max 15p (6 bereiche statt 8, je 5p)
2026-05-08 08:54:40 +02:00
libretech dde33f108f dhbw 01: fix pruefungsleistung link (git.librete.ch) 2026-05-08 08:46:23 +02:00
libretech 82407679b5 dhbw 01_web_eng: ecmascript-history slide (ecma international, ECMA-262, ES15, kompat-tabelle), html-anatomie ascii-diagramm (opening/closing tag, content, element, attribute), css-anatomie ascii-annotation (#description-beispiel mit pfeilen auf property+value), framework-logos als bg right:25% auf react/vue/svelte/astro/webpack/vite/parcel slides, a11y-test-slide layout fix (image inline statt bg-right wie hdm) 2026-05-07 17:21:25 +02:00
libretech c242e0f947 dhbw inhalt: vorlesungsplan tabelle → cards-grid (4×3, milestones hervorgehoben), über-mich logos-collage, internet-101 erweitert (timeline + arpanet-bg + WWW-1989 + mosaic-bg + unterseekabel + heute + five-eyes), a11y/WCAG-block (~12 slides nach native html, vor css), direkt-install slide gesplittet (szenario / symptome), code-block-color → #f48fb1 (alle 8 files), section.invert h2 #f48fb1 für barrierefreiheit, ressourcen-slide auf 01-05 nach cover, alle emojis raus (✓✗⚠️🍞🥬🏆 + ↔), ESM statt CJS in 03/04, librete.ch domain, kleinere fixes (chromium statt chrome, CLI aufgelöst, JetBrains edu, hoppscotch, SOLID-abk., node 24 LTS, brew/winget/scoop/choco-links, vim/neovim/emacs editoren, herzlich willkommen) 2026-05-07 17:09:04 +02:00
libretech 52312f1cb8 dhbw assets: arpanet 1969 + arpanet 90er, mosaic-screenshot, unterseekabel-karte für internet-101 + 5 a11y-demos (kontrast, fokus, error, semantik, keyboard) für barrierefreiheit-block 2026-05-07 17:08:46 +02:00
libretech e2bab75e60 dhbw assets: 13 logos (uni-stuttgart, dhbw, klett, pons, langenscheidt, nextmarkets, re-cinq, experience-one, becklyn, caesargustav) + collage.png für über-mich-slide, dark-varianten der weiß-auf-transparent-logos 2026-05-07 15:29:45 +02:00
libretech b242c7b4e6 223015c 02 + dhbw 01: klausurfolien markiert, prosa-sätze bei allen tabellen, layout-fixes
223015c/02:
- glossar landkarte: klausur, prosa, reihenfolge (host→client→server→req→res→protokoll), tcp/http als beispiel
- glossar adressen/daten/eigenschaften/protokolle: prosa-sätze
- tcp/ip-modell: nichtssagenden satz ersetzt
- dateneinheiten: merkhilfe durch prosa ersetzt
- osi vs tcp/ip: bg right statt h:440, prosa statt einzeiler
- port: tabelle→liste, prosa statt bullets
- ip vs mac vs port: prosa
- tcp vs udp, http-methoden, status-codes, einheiten: prosa
- selektoren id + farben: klausur markiert
- farben: mdn-link, prosa-satz

dhbw/01:
- farben: prosa-satz synchronisiert
- über-mich: collage-bild hinzugefügt
2026-05-07 15:21:40 +02:00
libretech 46b297ada4 dhbw 01_web_eng: direkt-install slide ausgebaut (szenario, symptome), du-formen entfernt (sachlich/akademisch), editor-liste vim/neovim/emacs (helix raus), git inline erklärt 2026-05-07 14:48:33 +02:00
libretech 8dabc4e570 dhbw farb-schema: himbeer (wie hdm 223015c) — highlight #d63384, h1 #a02060, h2 #1f2937 (#f48fb1 auf invert für barrierearmut), pre/code #0f0f23/#d63384, dimmed #4a4a6a, invert-bg #001520→#000; herzlich willkommen statt willkommen; git kurz erklärt 2026-05-07 14:41:07 +02:00
libretech 6b33a79d9c dhbw 01_web_eng: über-mich aktualisiert (systems & platform engineer, dhbw-mail), onboarding-block (werkzeuge/os, node-install pro os mit lts node 24, versionsmanager, alternative runtimes deno/bun, editor, browser-devtools, http-clients, git setup, projekt-skeleton), html-block aus hdm (was ist html, anatomie, metadaten, tags+attribute, native html), css-block aus hdm (selektoren, spezifität, box-modell, farben, einheiten, pseudo-klassen, responsive), mandala-PNG mit erklärungs-folie, font 1.7→1.4rem, semesterplan + prüfungsleistung details, ascii-browser-request raus (PNG bleibt), kleine fixes (chromium statt chrome, cli aufgelöst, brew-link, jetbrains edu, hoppscotch, solid abkürzungen aufgelöst) 2026-05-07 14:21:19 +02:00
libretech a07ab0a3d1 dhbw 02_css_extended: native html-block + basic-selektoren + basic-pseudoclasses + specificity raus (jetzt in 01), einheitliche cover (sitzung 2 — html & css frameworks), font 1.7→1.4rem, fragen-slide raus 2026-05-07 14:21:00 +02:00
libretech 5c25b14a59 dhbw 03-04 (node.js basics+advanced): cjs → esm in code-beispielen (import + node:-präfix), legacy-cjs als vergleichsbeispiel markiert, einheitliche cover, font 1.7→1.4rem, fragen-slides raus, __dirname/__filename via import.meta.url 2026-05-07 14:20:46 +02:00
libretech 79fbe046d9 dhbw 05-08 (testing/typescript/docker/best-practices): einheitliche cover (web engineering / dhbw stuttgart / sose 2026 · sitzung x), font 1.7→1.4rem, fragen+hausaufgabe-slides raus (kein klausur an dhbw) 2026-05-07 14:20:35 +02:00
libretech 4b267bcb8b dhbw assets: html/css demo-pngs aus hdm kopiert (20 files) + web-engineering-mandala.png 2026-05-07 14:20:20 +02:00
libretech df52a8c6a5 makefile: konsolidiere dev — nur 'make dev' (alle kurse, port 1312), per-course dev-targets + _PORT vars raus 2026-05-07 10:58:24 +02:00
libretech 6e41b8c3eb makefile: unified per-course pattern rules (build-/dev-/deploy-/klausur-<course>) statt hdm-/dhbw- duplikate, ~100 zeilen weg, neue kurse via variablen 2026-05-06 18:37:59 +02:00
libretech 96362e2959 dhbw: import original web-eng inhalte (testing, typescript, docker, best practices) + framework-übersicht, native html, prüfungsleistung-detail, sose 2026 2026-05-06 18:28:20 +02:00
libretech 73be35ccb8 dhbw: semesterplan sose 2026 (11 sitzungen) + deploy-path fix (kein doppeltes dhbw) 2026-05-06 17:03:44 +02:00
libretech 3c938b4b1c rebrand: hdm slides → uni slides (dhbw + hdm), make-targets mit hdm-/dhbw- prefix, dhbw_deploy_path typo fix 2026-05-04 20:05:50 +02:00
libretech 841a7ced76 dhbw: neuer kurs technik I — 4 kapitel + demo assets 2026-05-04 20:05:46 +02:00
libretech 5c419c9ed1 gendering: fehlende person-substantive in beiden kursen
- Nutzer/User -> Nutzende
- Endnutzer -> Endnutzende
- Teilnehmer -> Teilnehmende
- Programmierer/Entwickler -> Programmierende/Entwickelnde
- Web-Entwickler -> Web-Entwickelnde
- Tastatur-Nutzer -> Tastatur-Nutzende
- Benutzer -> Nutzende
- Konsumenten -> KonsumentInnen
- Künstler -> KünstlerInnen
- Autor -> AutorIn
- Fotografen -> FotografInnen
- Kunde -> KundIn

ausgenommen: code-identifiers (User, type User, /users/),
Sender/Empfänger (network protocol), Sawyer (konkrete person),
Hersteller/Betreiber (organisations-rolle).
2026-04-27 17:27:19 +02:00
libretech a955a7636a selbstlernen hex links: absolute urls fuer PDF-klick
PDF rendert relative pfade als nicht-klickbar. absolute https-urls
zu librete.ch/hdm/223015b/materials/hex1-5 funktionieren beide
in HTML- und PDF-export.
2026-04-27 16:29:09 +02:00
libretech c978842812 c01 Web-Zugänglichkeit Vertiefung: keine klausur, kein bg
- _class: 'erklaerung klausur' -> _class: erklaerung
- _backgroundColor: #fce4ec entfernt

ist nur eine vertiefung, keine klausur-folie.
2026-04-27 16:10:01 +02:00
libretech 61468d2d3a erklaerung: bg entfernen — soll vertiefung sein, nicht klausur
bug: 'section.erklaerung' hatte '@media print { background }' rule (alle 5
files), was im PDF rosa/blau-klausur-bg setzte. HTML hatte (in c) keinen
bg oder (in b) gradient bg. ergebnis: HTML zeigt erklaerung anders als PDF
— PDF sah aus wie klausurfolie obwohl es nur eine vertiefung ist.

fix: erklaerung behaelt nur 'font-size: 1.1rem' + h1 color/size + table
font-size. KEIN background mehr — weder screen noch print. visuell
unterschieden durch h1-color und font-size, nicht durch bg-farbe.

betroffen: b01, b02, b03, c01, c02 (alle files mit section.erklaerung).
2026-04-27 15:04:45 +02:00
libretech a3c3e259b4 b01: kaputte html-comments fixen (_class directive nicht im comment-prefix)
L603/L850: '<!-- _class: erklaerung' ohne '-->' und ' _class:' ohne '<!--'
in proper '<!--\n_class:\n-->' form gebracht. inhalt bleibt versteckt
wie vorher beabsichtigt, syntax aber jetzt sauber.
2026-04-27 01:26:31 +02:00
libretech ac39f2b746 klausurfragen pdf fix: pre blocks waren schwarz auf schwarz
bug: 'pre code { color: inherit }' fiel auf section --color-foreground (#1a1a2e)
zurueck weil 'pre' selbst keine 'color' hatte. multi-line code-bloecke
unsichtbar (nur hljs-syntax-spans hatten farbe, rest = dark on dark).

fix: 'pre { color: #f8f8f8 }' setzen + inline 'code' und hljs/non-hljs
selectoren analog zu chapter-styles ergaenzt.

verified: c klausurfragen page 60 (G2 mobile-first CSS pre-block) jetzt
voll lesbar.
2026-04-27 00:53:48 +02:00
libretech 19ca5ad3e0 223015b selbstlernen hex files: 5 statt 3 dateien, link wtf->hex, pfad ./materials
- referenzen wtf1/2/3 -> hex1/2/3 (dateinamen tatsächlich)
- hex4 (DOCX) und hex5 (ZIP) ergänzt — alle 5 vorhandenen materialien sichtbar
- pfad ../materials -> ./materials (build/223015b/materials struktur)
2026-04-26 22:05:48 +02:00
libretech aea96edd09 bit/byte/hex folien sync beide kurse + neue viz embeds
223015c (internettechnik):
- byte-aufbau ausgebaut (build-up tabelle 0..127..255 wie b)
- neue slides: why-8-bit, byte-flow, three-views nach byte-nibble-hex/hex-dec-table

223015b (dateiformate):
- byte-nibble-hex slide eingebaut
- neue slides: why-8-bit (vor hex-lead), hex-dec-table, byte-flow, three-views
- wo-begegnet-hex tabelle (CSS, MAC, fehlercodes, speicher, unicode)
- bit-vs-byte verwirrung (Mbit/s vs MB/s)

konvention: 'Byte' und 'Bit' invariant, kein plural-s
2026-04-26 22:05:32 +02:00
159 changed files with 11338 additions and 2516 deletions

No files matched your search

+4
View File
@@ -34,3 +34,7 @@ hdm-internettechnik-slides/
*.tmp *.tmp
*.bak *.bak
.idea .idea
# Interne Klausur-Previews – NIE committen/deployen
slides/**/*-INTERN.md
slides/*-INTERN.md
+28 -15
View File
@@ -1,11 +1,12 @@
# CLAUDE.md - Agent Guidelines for HdM Slides # CLAUDE.md - Agent Guidelines for Uni Slides (DHBW + HdM)
This project builds presentation decks for Marp, supporting multiple courses. This project builds presentation decks for Marp, supporting multiple courses.
## Courses ## Courses
- **223015b** - Dateiformate, Schnittstellen, Speichermedien (6 Kapitel) - **223015b** - Dateiformate, Schnittstellen, Speichermedien (HdM, 6 Kapitel + Klausur)
- **223015c** - Internettechnologien (3 Kapitel) - **223015c** - Internettechnologien (HdM, 3 Kapitel + Klausur)
- **dhbw** - Technik I – Grundlagen IT (DHBW, 8 Kapitel)
## Agent Restrictions ## Agent Restrictions
@@ -25,8 +26,9 @@ Slide files in `slides/*/*.md` are main content files:
``` ```
slides/ slides/
├── 223015b/ # Dateiformate course ├── 223015b/ # HdM: Dateiformate
└── 223015c/ # Internettechnik course ├── 223015c/ # HdM: Internettechnik
└── dhbw/ # DHBW: Technik I
scripts/ # Shared scripts scripts/ # Shared scripts
themes/ # Custom Marp themes themes/ # Custom Marp themes
build/ # Generated output (gitignored) build/ # Generated output (gitignored)
@@ -34,19 +36,30 @@ build/ # Generated output (gitignored)
## Build Commands ## Build Commands
Unified per-course pattern: `make <target>-<course>`. Group targets without
suffix run for all courses. Single dev server serves all courses.
```bash ```bash
make dev # Marp live server all courses (port 1312, HMR) # Dev (all courses, single port)
make dev-b # Marp live server 223015b (port 1313, HMR) make dev # Live server (HMR), port 1312
make dev-c # Marp live server 223015c (port 1314, HMR)
make build # Build all courses # Per-course build/deploy (replace <c> with course id: 223015b, 223015c, dhbw)
make build-b # Build 223015b only make build-<c> # Build HTML + PDF
make build-c # Build 223015c only make html-<c> # HTML only
make html # HTML only make pdf-<c> # PDF only
make pdf # PDF only make klausur-<c> # Extract klausur slides (HdM only)
make klausur # Extract klausur slides → slides/*/klausur.md make deploy-<c> # Build + deploy single course (ASK FIRST!)
make deploy # Deploy all (ASK FIRST!)
# All courses
make build # Build everything
make html / pdf # HTML / PDF only
make klausur # Extract klausur (HdM courses only)
make deploy # Deploy everything (ASK FIRST!)
``` ```
**Adding a new course:** add id to `COURSES` in Makefile + define `<id>_NAME`,
`<id>_KAPITEL`, `<id>_DEPLOY`, `<id>_KLAUSUR`. No new targets needed.
## Nix Flake Commands ## Nix Flake Commands
```bash ```bash
+154 -195
View File
@@ -1,228 +1,187 @@
# HdM Slides - Unified Makefile # Uni Slides – Unified Makefile
# Supports multiple courses: 223015b (Dateiformate) and 223015c (Internettechnik) # Multi-course Marp build system (HdM + DHBW)
.PHONY: help dev dev-b dev-c build build-b build-c pdf html klausur clean install deploy qr optimize-images .PHONY: help dev build html pdf klausur deploy deploy-index build-index \
clean install qr qr-slides optimize-images
# Course configuration # -----------------------------------------------------------------------------
COURSES = 223015b 223015c # Course registry
# -----------------------------------------------------------------------------
COURSES = 223015b 223015c dhbw
SLIDES_DIR = slides SLIDES_DIR = slides
# Course-specific settings
223015b_NAME = Dateiformate, Schnittstellen, Speichermedien
223015b_KAPITEL = 00-intro 01-grundlagen-text-audio 02-bild-audio-video 03-speichermedien-schnittstellen 04-distribution-apis-zukunft 05-vertiefung-offene-fragen klausurfolien klausurfragen
223015b_DEPLOY_PATH = /home/tengo/html/hdm/223015b
223015c_NAME = Internettechnologien
223015c_KAPITEL = 01-geschichte-grundlagen-html 02-netzwerke-protokolle-css 03-interaktivitaet-javascript klausurfolien klausurfragen
223015c_DEPLOY_PATH = /home/tengo/html/hdm/223015c
DEPLOY_HOST = tengo@tuttle.uberspace.de DEPLOY_HOST = tengo@tuttle.uberspace.de
# Default target # Per-course settings: NAME, KAPITEL, DEPLOY, PORT, KLAUSUR (1 or empty)
223015b_NAME = Dateiformate, Schnittstellen, Speichermedien
223015b_KAPITEL = 00-intro 01-grundlagen-text-audio 02-bild-audio-video 03-speichermedien-schnittstellen 04-distribution-apis-zukunft 05-vertiefung-offene-fragen klausurfolien klausurfragen
223015b_DEPLOY = /home/tengo/html/hdm/223015b
223015b_KLAUSUR = 1
223015c_NAME = Internettechnologien
223015c_KAPITEL = 01-geschichte-grundlagen-html 02-netzwerke-protokolle-css klausurfolien klausurfragen
223015c_DEPLOY = /home/tengo/html/hdm/223015c
223015c_KLAUSUR = 1
dhbw_NAME = Web Engineering
dhbw_KAPITEL = 01-intro 02-html-css 03-js-frameworks 04-nodejs-basics 05-express 06-testing 07-typescript 08-docker 09-best-practices
dhbw_DEPLOY = /home/tengo/html/dhbw
dhbw_KLAUSUR =
# Courses with klausur extraction enabled
KLAUSUR_COURSES = $(foreach c,$(COURSES),$(if $($(c)_KLAUSUR),$(c),))
ROOT_DEPLOY = /home/tengo/html/hdm
# -----------------------------------------------------------------------------
# Help
# -----------------------------------------------------------------------------
help: help:
@echo "HdM Slides - Unified Build System" @echo "Uni Slides – Unified Build System"
@echo "" @echo ""
@echo "Courses:" @echo "Courses:"
@echo " 223015b - Dateiformate, Schnittstellen, Speichermedien" @for c in $(COURSES); do echo " $$c"; done
@echo " 223015c - Internettechnologien"
@echo "" @echo ""
@echo "Development:" @echo "Dev:"
@echo " make dev - Marp live server all courses (port 1312, HMR)" @echo " make dev Live server, all courses (port 1312)"
@echo " make dev-b - Marp live server 223015b (port 1313, HMR)"
@echo " make dev-c - Marp live server 223015c (port 1314, HMR)"
@echo "" @echo ""
@echo "Build:" @echo "Per-course build/deploy (replace <c> with course id):"
@echo " make build - Build all courses" @echo " make build-<c> Build HTML + PDF"
@echo " make build-b - Build 223015b only" @echo " make html-<c> Build HTML only"
@echo " make build-c - Build 223015c only" @echo " make pdf-<c> Build PDF only"
@echo " make pdf - Export all to PDF" @echo " make klausur-<c> Extract klausur slides"
@echo " make html - Export all to HTML" @echo " make deploy-<c> Build + deploy"
@echo " make klausur - Extract klausurfolien slides" @echo ""
@echo "All courses:"
@echo " make build Build everything"
@echo " make html / pdf HTML / PDF only"
@echo " make klausur Extract klausur (HdM courses)"
@echo " make deploy Deploy everything"
@echo "" @echo ""
@echo "Tools:" @echo "Tools:"
@echo " make qr URL=... - Generate QR code for URL" @echo " make qr URL=... QR code"
@echo " make optimize-images COURSE=223015b - Resize images" @echo " make qr-slides COURSE=<c> QR for course URL"
@echo "" @echo " make optimize-images COURSE=<c> Resize images"
@echo "Deploy:" @echo " make clean Remove generated files"
@echo " make deploy - Deploy all courses" @echo " make install npm install"
@echo " make deploy-b - Deploy 223015b only"
@echo " make deploy-c - Deploy 223015c only"
@echo ""
@echo "Other:"
@echo " make clean - Remove generated files"
@echo " make install - Install dependencies"
# Ensure build directories exist # -----------------------------------------------------------------------------
# Build infrastructure
# -----------------------------------------------------------------------------
build/.exists: build/.exists:
@mkdir -p build/223015b build/223015c @mkdir -p $(addprefix build/,$(COURSES))
@touch $@ @touch $@
# Development server (Marp live server with HMR) # -----------------------------------------------------------------------------
# Pattern rules: build-<c>, html-<c>, pdf-<c>, klausur-<c>, deploy-<c>
# Per-course config read via $($*_VAR)
# -----------------------------------------------------------------------------
build-%: build/.exists
@echo "Building $*..."
@mkdir -p build/$*
@cp -r $(SLIDES_DIR)/$*/assets build/$*/ 2>/dev/null || true
@cp -r $(SLIDES_DIR)/$*/materials build/$*/ 2>/dev/null || true
@for f in $($*_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$*/$$f.md" ]; then \
echo " $$f"; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$*/$$f.md -o build/$*/$$f.html; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$*/$$f.md --pdf --allow-local-files -o build/$*/$$f.pdf; \
fi \
done
@./scripts/generate-index.sh $* build/$*
html-%: build/.exists
@echo "Building HTML for $*..."
@mkdir -p build/$*
@cp -r $(SLIDES_DIR)/$*/assets build/$*/ 2>/dev/null || true
@cp -r $(SLIDES_DIR)/$*/materials build/$*/ 2>/dev/null || true
@for f in $($*_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$*/$$f.md" ]; then \
echo " $$f.html"; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$*/$$f.md -o build/$*/$$f.html; \
fi \
done
@./scripts/generate-index.sh $* build/$*
pdf-%: build/.exists
@echo "Building PDFs for $*..."
@mkdir -p build/$*
@for f in $($*_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$*/$$f.md" ]; then \
echo " $$f.pdf"; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$*/$$f.md --pdf --allow-local-files -o build/$*/$$f.pdf; \
fi \
done
klausur-%:
@./scripts/extract-klausur.sh $*
deploy-%: build-%
@echo "Deploying $*..."
scp build/$*/*.html $(DEPLOY_HOST):$($*_DEPLOY)/
scp build/$*/*.pdf $(DEPLOY_HOST):$($*_DEPLOY)/ 2>/dev/null || true
scp build/$*/*.svg $(DEPLOY_HOST):$($*_DEPLOY)/ 2>/dev/null || true
scp -r build/$*/assets/ $(DEPLOY_HOST):$($*_DEPLOY)/ 2>/dev/null || true
scp -r build/$*/materials/ $(DEPLOY_HOST):$($*_DEPLOY)/ 2>/dev/null || true
@echo "$* deployed."
# -----------------------------------------------------------------------------
# Group targets (all courses)
# -----------------------------------------------------------------------------
dev: dev:
@./scripts/dev-server.sh @./scripts/dev-server.sh
dev-b: build: $(addprefix build-,$(COURSES))
@./scripts/dev-server.sh 223015b 1313 html: $(addprefix html-,$(COURSES))
pdf: $(addprefix pdf-,$(COURSES))
dev-c: klausur: $(addprefix klausur-,$(KLAUSUR_COURSES))
@./scripts/dev-server.sh 223015c 1314
dev-kill:
@-fuser -k 1312/tcp 2>/dev/null || true
@-fuser -k 1313/tcp 2>/dev/null || true
@-fuser -k 1314/tcp 2>/dev/null || true
# Build functions
define build_course
@echo "Building $(1)..."
@mkdir -p build/$(1)
@cp -r $(SLIDES_DIR)/$(1)/assets build/$(1)/ 2>/dev/null || true
@cp -r $(SLIDES_DIR)/$(1)/materials build/$(1)/ 2>/dev/null || true
@for f in $($(1)_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$(1)/$$f.md" ]; then \
echo " Building $$f..."; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$(1)/$$f.md -o build/$(1)/$$f.html; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$(1)/$$f.md --pdf --allow-local-files -o build/$(1)/$$f.pdf; \
fi \
done
@./scripts/generate-index.sh $(1) build/$(1)
endef
build-b: build/.exists
$(call build_course,223015b)
build-c: build/.exists
$(call build_course,223015c)
build: build-b build-c
@echo "All courses built!"
# HTML only builds
define html_course
@echo "Building HTML for $(1)..."
@mkdir -p build/$(1)
@cp -r $(SLIDES_DIR)/$(1)/assets build/$(1)/ 2>/dev/null || true
@cp -r $(SLIDES_DIR)/$(1)/materials build/$(1)/ 2>/dev/null || true
@for f in $($(1)_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$(1)/$$f.md" ]; then \
echo " $$f.html"; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$(1)/$$f.md -o build/$(1)/$$f.html; \
fi \
done
@./scripts/generate-index.sh $(1) build/$(1)
endef
html-b: build/.exists
$(call html_course,223015b)
html-c: build/.exists
$(call html_course,223015c)
html: html-b html-c
# PDF only builds
define pdf_course
@echo "Building PDFs for $(1)..."
@mkdir -p build/$(1)
@for f in $($(1)_KAPITEL); do \
if [ -f "$(SLIDES_DIR)/$(1)/$$f.md" ]; then \
echo " $$f.pdf"; \
npx @marp-team/marp-cli $(SLIDES_DIR)/$(1)/$$f.md --pdf --allow-local-files -o build/$(1)/$$f.pdf; \
fi \
done
endef
pdf-b: build/.exists
$(call pdf_course,223015b)
pdf-c: build/.exists
$(call pdf_course,223015c)
pdf: pdf-b pdf-c
# Klausur slide extraction
klausur-b:
@./scripts/extract-klausur.sh 223015b
klausur-c:
@./scripts/extract-klausur.sh 223015c
klausur: klausur-b klausur-c
@echo "Klausurfolien slides extracted for all courses!"
# QR Code generation (uses nix-shell)
qr:
ifndef URL
@echo "Usage: make qr URL=https://example.com"
@echo " make qr URL=https://example.com OUTPUT=my-qr.png"
else
@nix-shell -p qrencode --run 'qrencode -o $(or $(OUTPUT),qr-code.png) -s 10 "$(URL)"'
@echo "Generated: $(or $(OUTPUT),qr-code.png)"
endif
# QR code for a slide deck URL
qr-slides:
ifndef COURSE
@echo "Usage: make qr-slides COURSE=223015b"
else
@nix-shell -p qrencode --run 'qrencode -o build/$(COURSE)/qr-$(COURSE).png -s 10 "https://librete.ch/hdm/$(COURSE)/"'
@echo "Generated: build/$(COURSE)/qr-$(COURSE).png"
endif
# Optimize images
optimize-images:
ifndef COURSE
@echo "Usage: make optimize-images COURSE=223015b"
else
@echo "Optimizing images for $(COURSE)..."
@mkdir -p $(SLIDES_DIR)/$(COURSE)/assets-original
@nix-shell -p imagemagick --run 'for img in $(SLIDES_DIR)/$(COURSE)/assets/*.png $(SLIDES_DIR)/$(COURSE)/assets/*.jpg 2>/dev/null; do \
[ -f "$$img" ] && cp "$$img" $(SLIDES_DIR)/$(COURSE)/assets-original/ && \
magick "$$img" -resize "1920x>" -quality 85 "$$img" && echo " Optimized: $$(basename $$img)"; \
done || true'
@echo "Done! Originals in assets-original/"
endif
# Root index for /hdm/
HDM_DEPLOY_PATH = /home/tengo/html/hdm
build-index: build/.exists build-index: build/.exists
@echo "Building root index..." @echo "Building root index..."
@mkdir -p build @mkdir -p build
@./scripts/generate-root-index.sh @./scripts/generate-root-index.sh
# Deploy
define deploy_course
@echo "Deploying $(1)..."
scp build/$(1)/*.html $(DEPLOY_HOST):$($(1)_DEPLOY_PATH)/
scp build/$(1)/*.pdf $(DEPLOY_HOST):$($(1)_DEPLOY_PATH)/ 2>/dev/null || true
scp build/$(1)/*.svg $(DEPLOY_HOST):$($(1)_DEPLOY_PATH)/ 2>/dev/null || true
scp -r build/$(1)/assets/ $(DEPLOY_HOST):$($(1)_DEPLOY_PATH)/
scp -r build/$(1)/materials/ $(DEPLOY_HOST):$($(1)_DEPLOY_PATH)/ 2>/dev/null || true
@echo "$(1) deployed!"
endef
deploy-b: build-b
$(call deploy_course,223015b)
deploy-c: build-c
$(call deploy_course,223015c)
deploy-index: build-index deploy-index: build-index
@echo "Deploying root index..." @echo "Deploying root index..."
scp build/index.html $(DEPLOY_HOST):$(HDM_DEPLOY_PATH)/ scp build/index.html $(DEPLOY_HOST):$(ROOT_DEPLOY)/ 2>/dev/null || true
scp build/qr-root.svg $(DEPLOY_HOST):$(HDM_DEPLOY_PATH)/ 2>/dev/null || true @echo "Root index deployed."
@echo "Root index deployed!"
deploy: build-index deploy-b deploy-c deploy-index deploy: build-index $(addprefix deploy-,$(COURSES)) deploy-index
@echo "All courses deployed!" @echo "All courses deployed."
# -----------------------------------------------------------------------------
# Tools
# -----------------------------------------------------------------------------
qr:
ifndef URL
@echo "Usage: make qr URL=https://example.com [OUTPUT=qr.png]"
else
@nix-shell -p qrencode --run 'qrencode -o $(or $(OUTPUT),qr-code.png) -s 10 "$(URL)"'
@echo "Generated: $(or $(OUTPUT),qr-code.png)"
endif
qr-slides:
ifndef COURSE
@echo "Usage: make qr-slides COURSE=<course>"
else
@nix-shell -p qrencode --run 'qrencode -o build/$(COURSE)/qr-$(COURSE).png -s 10 "https://librete.ch/hdm/$(COURSE)/"'
@echo "Generated: build/$(COURSE)/qr-$(COURSE).png"
endif
optimize-images:
ifndef COURSE
@echo "Usage: make optimize-images COURSE=<course>"
else
@echo "Optimizing images for $(COURSE)..."
@mkdir -p $(SLIDES_DIR)/$(COURSE)/assets-original
@nix-shell -p imagemagick --run 'for img in $(SLIDES_DIR)/$(COURSE)/assets/*.png $(SLIDES_DIR)/$(COURSE)/assets/*.jpg 2>/dev/null; do \
[ -f "$$img" ] && cp "$$img" $(SLIDES_DIR)/$(COURSE)/assets-original/ && \
magick "$$img" -resize "1920x>" -quality 85 "$$img" && echo " $$(basename $$img)"; \
done || true'
@echo "Done. Originals in assets-original/"
endif
# Clean
clean: clean:
@echo "Cleaning generated files..." @echo "Cleaning..."
rm -rf build/ .dev-index/ *.pdf *.html qr-code.png rm -rf build/ .dev-index/ *.pdf *.html qr-code.png
# Install dependencies
install: install:
@echo "Installing dependencies..." @npm install
npm install
+30 -38
View File
@@ -1,25 +1,22 @@
# HdM Slides # Uni Slides - DHBW + HdM
Presentation slides for HdM Stuttgart courses, built with [Marp](https://marp.app/). Combined presentation slides for DHBW and HdM Stuttgart courses, built with [Marp](https://marp.app/).
## Courses ## Courses
| Code | Title | Kapitel | | Code | Title | Origin |
|------|-------|---------| |------|-------|--------|
| 223015b | Dateiformate, Schnittstellen, Speichermedien | 6 | | 223015b | Dateiformate, Schnittstellen, Speichermedien | HdM |
| 223015c | Internettechnologien | 3 | | 223015c | Internettechnologien | HdM |
| dhbw | Technik I – Grundlagen IT | DHBW |
## Project Structure ## Project Structure
``` ```
slides/ slides/
├── 223015b/ # Dateiformate course ├── 223015b/ # HdM: Dateiformate
│ ├── 00-intro.md ├── 223015c/ # HdM: Internettechnik
│ ├── 01-grundlagen-text-audio.md ├── dhbw/ # DHBW slides (TBD)
│ └── ...
└── 223015c/ # Internettechnik course
├── 01-geschichte-grundlagen-html.md
└── ...
scripts/ # Build scripts scripts/ # Build scripts
build/ # Generated output (gitignored) build/ # Generated output (gitignored)
``` ```
@@ -27,54 +24,49 @@ build/ # Generated output (gitignored)
## Development ## Development
```bash ```bash
# Start development server (hot reload) make hdm-dev # Live server all HdM courses (port 1312, HMR)
make dev # Single server for all courses (port 3000) make hdm-dev-b # 223015b only (port 1313, HMR)
make hdm-dev-c # 223015c only (port 1314, HMR)
# Access individual courses: make dhbw-dev # DHBW (not yet configured)
# 223015b: http://localhost:3000/223015b/
# 223015c: http://localhost:3000/223015c/
``` ```
## Build ## Build
```bash ```bash
make build # Build all (HTML + PDF) make hdm-build # Build all HdM courses (HTML + PDF)
make build-b # Build 223015b only make hdm-build-b # 223015b only
make build-c # Build 223015c only make hdm-build-c # 223015c only
make html # HTML only make hdm-html # HTML only all HdM
make pdf # PDF only make hdm-pdf # PDF only all HdM
``` ```
## Klausur Folien ## Klausur Folien
Extract exam-relevant slides (marked with `<!-- _class: klausur -->`) into a single file: Extract exam slides (`<!-- _class: klausur -->`) into single file:
```bash ```bash
make klausur # Extract all courses make hdm-klausur # All HdM courses
make klausur-b # 223015b only make hdm-klausur-b # 223015b only
make klausur-c # 223015c only make hdm-klausur-c # 223015c only
``` ```
Output: `slides/<course>/klausurfolien.md` Output: `slides/<course>/klausurfolien.md`
The generated file includes:
- A title slide per kapitel for orientation
- All slides marked with `<!-- _class: klausur -->`
## Tools ## Tools
```bash ```bash
make qr URL=https://example.com # Generate QR code make qr URL=https://example.com # Generate QR code
make qr-slides COURSE=223015b # QR for course URL make qr-slides COURSE=223015b # QR for course URL
make optimize-images COURSE=223015b # Resize images make optimize-images COURSE=223015b # Resize images
``` ```
## Deploy ## Deploy
```bash ```bash
make deploy # Deploy all courses make hdm-deploy # Deploy all HdM courses
make deploy-b # Deploy 223015b only make hdm-deploy-b # 223015b only
make deploy-c # Deploy 223015c only make hdm-deploy-c # 223015c only
make deploy # Full deploy (all courses)
``` ```
## Marking Slides as Klausur-Relevant ## Marking Slides as Klausur-Relevant
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
description = "HdM Slides - Marp presentation builder"; description = "Uni Slides - DHBW + HdM Marp presentation builder";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -19,7 +19,7 @@
]; ];
shellHook = '' shellHook = ''
echo "HdM Slides - run 'make' for available commands" echo "Uni Slides - run 'make' for available commands"
''; '';
}; };
} }
+159 -29
View File
@@ -13,6 +13,15 @@ case "$COURSE" in
SLIDES_URL="https://librete.ch/hdm/223015b" SLIDES_URL="https://librete.ch/hdm/223015b"
ACCENT_COLOR="#1e5f8a" ACCENT_COLOR="#1e5f8a"
ACCENT_LIGHT="#e8f4fc" ACCENT_LIGHT="#e8f4fc"
INSTITUTION="HdM Stuttgart"
HEADLINE="HdM Vorlesungen"
KAPITEL_TERM="Kapitel"
BREADCRUMB_URL="../"
EXAM_URL=""
EXAM_LABEL=""
KONTAKT_URL="mailto:mail@librete.ch"
KONTAKT_LABEL="Kontakt"
QR_IMG="qr-index.svg"
;; ;;
223015c) 223015c)
TITLE="Grundlagen IT- und Internettechnik" TITLE="Grundlagen IT- und Internettechnik"
@@ -20,16 +29,72 @@ case "$COURSE" in
SLIDES_URL="https://librete.ch/hdm/223015c" SLIDES_URL="https://librete.ch/hdm/223015c"
ACCENT_COLOR="#d63384" ACCENT_COLOR="#d63384"
ACCENT_LIGHT="#fce4ec" ACCENT_LIGHT="#fce4ec"
INSTITUTION="HdM Stuttgart"
HEADLINE="HdM Vorlesungen"
KAPITEL_TERM="Kapitel"
BREADCRUMB_URL="../"
EXAM_URL=""
EXAM_LABEL=""
KONTAKT_URL="mailto:mail@librete.ch"
KONTAKT_LABEL="Kontakt"
QR_IMG="qr-index.svg"
;;
dhbw)
TITLE="Web Engineering"
SUBTITLE="Informatik / Wirtschaftsinformatik"
SLIDES_URL="https://librete.ch/dhbw"
ACCENT_COLOR="#a02060"
ACCENT_LIGHT="#fce4ec"
INSTITUTION="DHBW Stuttgart"
HEADLINE="DHBW Vorlesungen"
KAPITEL_TERM="Sitzung"
BREADCRUMB_URL=""
EXAM_URL="https://git.librete.ch/DHBW/pruefungsleistung"
EXAM_LABEL="Prüfungsleistung"
KONTAKT_URL="mailto:michael.czechowski@lehre.dhbw-stuttgart.de"
KONTAKT_LABEL="Kontakt"
QR_IMG="assets/slides-dhbw.png"
;; ;;
*) *)
TITLE="$COURSE - HdM Slides" TITLE="$COURSE - Slides"
SUBTITLE="Lecture Slides" SUBTITLE="Lecture Slides"
SLIDES_URL="https://librete.ch/hdm/$COURSE" SLIDES_URL="https://librete.ch/$COURSE"
ACCENT_COLOR="#0066cc" ACCENT_COLOR="#0066cc"
ACCENT_LIGHT="#e8f4fc" ACCENT_LIGHT="#e8f4fc"
INSTITUTION="Stuttgart"
HEADLINE="Vorlesungen"
KAPITEL_TERM="Kapitel"
BREADCRUMB_URL=""
EXAM_URL=""
EXAM_LABEL=""
KONTAKT_URL="mailto:mail@librete.ch"
KONTAKT_LABEL="Kontakt"
QR_IMG="qr-index.svg"
;; ;;
esac esac
# Optional breadcrumb HTML (empty if no parent overview)
if [[ -n "$BREADCRUMB_URL" ]]; then
BREADCRUMB_HTML="<p class=\"breadcrumb\"><a href=\"$BREADCRUMB_URL\">← Kursübersicht</a></p>"
else
BREADCRUMB_HTML=""
fi
# Optional exam-link HTML
if [[ -n "$EXAM_URL" ]]; then
EXAM_HTML="<p class=\"exam-link\"><a href=\"$EXAM_URL\">$EXAM_LABEL →</a></p>"
else
EXAM_HTML=""
fi
# Footer kursübersicht link only when breadcrumb parent exists
if [[ -n "$BREADCRUMB_URL" ]]; then
FOOTER_KURS_HTML=" ·
<a href=\"$BREADCRUMB_URL\">Kursübersicht</a>"
else
FOOTER_KURS_HTML=""
fi
# Topic mappings for nice German titles # Topic mappings for nice German titles
declare -A TOPIC_MAP declare -A TOPIC_MAP
TOPIC_MAP["intro"]="Einführung" TOPIC_MAP["intro"]="Einführung"
@@ -41,6 +106,14 @@ TOPIC_MAP["vertiefung-offene-fragen"]="Vertiefung & Offene Fragen"
TOPIC_MAP["geschichte-grundlagen-html"]="Geschichte, Grundlagen & HTML" TOPIC_MAP["geschichte-grundlagen-html"]="Geschichte, Grundlagen & HTML"
TOPIC_MAP["netzwerke-protokolle-css"]="Netzwerke, Protokolle & CSS" TOPIC_MAP["netzwerke-protokolle-css"]="Netzwerke, Protokolle & CSS"
TOPIC_MAP["interaktivitaet-javascript"]="Interaktivität & JavaScript" TOPIC_MAP["interaktivitaet-javascript"]="Interaktivität & JavaScript"
TOPIC_MAP["html-css"]="HTML & CSS"
TOPIC_MAP["js-frameworks"]="JS, Frameworks & Build Tools"
TOPIC_MAP["nodejs-basics"]="Node.js: Scripting, Running & Building"
TOPIC_MAP["express"]="Express API, CRUD & Middlewares"
TOPIC_MAP["testing"]="Testing"
TOPIC_MAP["typescript"]="TypeScript"
TOPIC_MAP["docker"]="Docker, Proxies & DBs"
TOPIC_MAP["best-practices"]="Wrap-up & Best Practices"
# Configure which topics should appear disabled per course # Configure which topics should appear disabled per course
# Two modes: fully disabled (non-clickable card) and buttons-disabled (link remains clickable, buttons are disabled) # Two modes: fully disabled (non-clickable card) and buttons-disabled (link remains clickable, buttons are disabled)
@@ -48,8 +121,17 @@ declare -A DISABLED_FULL
declare -A DISABLED_BUTTONS declare -A DISABLED_BUTTONS
# For course B: chapters 3,4,5 should be fully disabled # For course B: chapters 3,4,5 should be fully disabled
DISABLED_FULL["223015b"]="speichermedien-schnittstellen distribution-apis-zukunft vertiefung-offene-fragen" DISABLED_FULL["223015b"]="speichermedien-schnittstellen distribution-apis-zukunft vertiefung-offene-fragen"
# For course C: chapter 3 should keep link clickable but buttons disabled # For course C: all chapters fully active
DISABLED_BUTTONS["223015c"]="interaktivitaet-javascript" DISABLED_BUTTONS["223015c"]=""
# Courses where the "Klausurrelevante Folien" card should be active (clickable)
declare -A KLAUSURFOLIEN_ACTIVE
KLAUSURFOLIEN_ACTIVE["223015b"]=1
# Courses where the "Klausurfragen" card should be active (clickable)
declare -A KLAUSURFRAGEN_ACTIVE
KLAUSURFRAGEN_ACTIVE["223015b"]=1
KLAUSURFRAGEN_ACTIVE["223015c"]=1
cat > "$BUILD_DIR/index.html" << HEADER cat > "$BUILD_DIR/index.html" << HEADER
<!DOCTYPE html> <!DOCTYPE html>
@@ -101,6 +183,16 @@ cat > "$BUILD_DIR/index.html" << HEADER
font-size: 0.875rem; font-size: 0.875rem;
margin-top: 0.25rem; margin-top: 0.25rem;
} }
.exam-link {
margin-top: 1rem;
font-size: 0.95rem;
}
.exam-link a {
color: $ACCENT_COLOR;
text-decoration: none;
font-weight: 500;
}
.exam-link a:hover { text-decoration: underline; }
/* Use the .termine wrapper and add a gap between cards */ /* Use the .termine wrapper and add a gap between cards */
.termine { .termine {
margin-top: 1.5rem; margin-top: 1.5rem;
@@ -226,11 +318,12 @@ cat > "$BUILD_DIR/index.html" << HEADER
</head> </head>
<body> <body>
<header> <header>
<p class="breadcrumb"><a href="../">← Kursübersicht</a></p> $BREADCRUMB_HTML
<h1>HdM Vorlesungen</h1> <h1>$HEADLINE</h1>
<h2 class="course-heading">$TITLE</h2> <h2 class="course-heading">$TITLE</h2>
<p class="subtitle">$SUBTITLE</p> <p class="subtitle">$SUBTITLE</p>
<p class="meta">HdM Stuttgart · Sommersemester 2026 · Michael Czechowski</p> <p class="meta">$INSTITUTION · Sommersemester 2026 · Michael Czechowski</p>
$EXAM_HTML
</header> </header>
<div class="termine"> <div class="termine">
HEADER HEADER
@@ -253,7 +346,7 @@ for html in $(ls "$BUILD_DIR"/[0-9][0-9]-*.html 2>/dev/null | sort); do
if [[ "$kapitel_num" == "0" ]] || [[ -z "$kapitel_num" ]]; then if [[ "$kapitel_num" == "0" ]] || [[ -z "$kapitel_num" ]]; then
kapitel_label="Einführung" kapitel_label="Einführung"
else else
kapitel_label="Kapitel $kapitel_num" kapitel_label="$KAPITEL_TERM $kapitel_num"
fi fi
pdf_filename="${filename%.html}.pdf" pdf_filename="${filename%.html}.pdf"
@@ -320,13 +413,29 @@ LINK
done done
# Add klausurfolien entry if it exists (disabled — wird noch überarbeitet) # Add klausurfolien entry if it exists (active per KLAUSURFOLIEN_ACTIVE, else disabled)
if [[ -f "$BUILD_DIR/klausurfolien.html" ]]; then if [[ -f "$BUILD_DIR/klausurfolien.html" ]]; then
pdf_disabled="" if [[ "${KLAUSURFOLIEN_ACTIVE[$COURSE]}" == "1" ]]; then
if [[ -f "$BUILD_DIR/klausurfolien.pdf" ]]; then pdf_active=""
pdf_disabled="<span class=\"btn btn-pdf btn-disabled\">PDF</span>" if [[ -f "$BUILD_DIR/klausurfolien.pdf" ]]; then
fi pdf_active="<a href=\"klausurfolien.pdf\" class=\"btn btn-pdf\">PDF</a>"
cat >> "$BUILD_DIR/index.html" << KLAUSUR fi
cat >> "$BUILD_DIR/index.html" << KLAUSUR
<div class="kapitel-card" style="border-left: 3px solid $ACCENT_COLOR;">
<a href="klausurfolien.html" class="kapitel-link">
<div class="kapitel-label">Prüfung</div>
<div class="kapitel-title">Klausurrelevante Folien</div>
</a>
<a href="klausurfolien.html" class="btn btn-slides">Folien</a>
$pdf_active
</div>
KLAUSUR
else
pdf_disabled=""
if [[ -f "$BUILD_DIR/klausurfolien.pdf" ]]; then
pdf_disabled="<span class=\"btn btn-pdf btn-disabled\">PDF</span>"
fi
cat >> "$BUILD_DIR/index.html" << KLAUSUROFF
<div class="kapitel-card disabled" style="border-left: 3px solid $ACCENT_COLOR;"> <div class="kapitel-card disabled" style="border-left: 3px solid $ACCENT_COLOR;">
<div class="kapitel-link"> <div class="kapitel-link">
<div class="kapitel-label">Prüfung</div> <div class="kapitel-label">Prüfung</div>
@@ -335,20 +444,41 @@ if [[ -f "$BUILD_DIR/klausurfolien.html" ]]; then
<span class="btn btn-slides btn-disabled">Folien</span> <span class="btn btn-slides btn-disabled">Folien</span>
$pdf_disabled $pdf_disabled
</div> </div>
KLAUSUR KLAUSUROFF
fi
fi fi
# Add klausurfragen entry if it exists (disabled — wird noch überarbeitet) # Add klausurfragen entry if it exists (active per KLAUSURFRAGEN_ACTIVE, else disabled)
if [[ -f "$BUILD_DIR/klausurfragen.html" ]] || [[ -f "$BUILD_DIR/klausurfragen.pdf" ]]; then if [[ -f "$BUILD_DIR/klausurfragen.html" ]] || [[ -f "$BUILD_DIR/klausurfragen.pdf" ]]; then
pdf_disabled="" if [[ "${KLAUSURFRAGEN_ACTIVE[$COURSE]}" == "1" ]]; then
if [[ -f "$BUILD_DIR/klausurfragen.pdf" ]]; then pdf_active=""
pdf_disabled="<span class=\"btn btn-pdf btn-disabled\">PDF</span>" if [[ -f "$BUILD_DIR/klausurfragen.pdf" ]]; then
fi pdf_active="<a href=\"klausurfragen.pdf\" class=\"btn btn-pdf\">PDF</a>"
slides_disabled="" fi
if [[ -f "$BUILD_DIR/klausurfragen.html" ]]; then slides_active=""
slides_disabled="<span class=\"btn btn-slides btn-disabled\">Folien</span>" if [[ -f "$BUILD_DIR/klausurfragen.html" ]]; then
fi slides_active="<a href=\"klausurfragen.html\" class=\"btn btn-slides\">Folien</a>"
cat >> "$BUILD_DIR/index.html" << KLAUSURFRAGEN fi
cat >> "$BUILD_DIR/index.html" << KLAUSURFRAGEN
<div class="kapitel-card" style="border-left: 3px solid $ACCENT_COLOR;">
<a href="klausurfragen.html" class="kapitel-link">
<div class="kapitel-label">Prüfung</div>
<div class="kapitel-title">Klausurfragen</div>
</a>
$slides_active
$pdf_active
</div>
KLAUSURFRAGEN
else
pdf_disabled=""
if [[ -f "$BUILD_DIR/klausurfragen.pdf" ]]; then
pdf_disabled="<span class=\"btn btn-pdf btn-disabled\">PDF</span>"
fi
slides_disabled=""
if [[ -f "$BUILD_DIR/klausurfragen.html" ]]; then
slides_disabled="<span class=\"btn btn-slides btn-disabled\">Folien</span>"
fi
cat >> "$BUILD_DIR/index.html" << KLAUSURFRAGENOFF
<div class="kapitel-card disabled" style="border-left: 3px solid $ACCENT_COLOR;"> <div class="kapitel-card disabled" style="border-left: 3px solid $ACCENT_COLOR;">
<div class="kapitel-link"> <div class="kapitel-link">
<div class="kapitel-label">Prüfung</div> <div class="kapitel-label">Prüfung</div>
@@ -357,18 +487,18 @@ if [[ -f "$BUILD_DIR/klausurfragen.html" ]] || [[ -f "$BUILD_DIR/klausurfragen.p
$slides_disabled $slides_disabled
$pdf_disabled $pdf_disabled
</div> </div>
KLAUSURFRAGEN KLAUSURFRAGENOFF
fi
fi fi
cat >> "$BUILD_DIR/index.html" << FOOTER cat >> "$BUILD_DIR/index.html" << FOOTER
</div> </div>
<div class="qr-section"> <div class="qr-section">
<img src="qr-index.svg" alt="QR Code" class="qr-code"> <img src="$QR_IMG" alt="QR Code" class="qr-code">
<p class="qr-url">$SLIDES_URL</p> <p class="qr-url">$SLIDES_URL</p>
</div> </div>
<footer> <footer>
<a href="mailto:mail@librete.ch">Kontakt</a> · <a href="$KONTAKT_URL">$KONTAKT_LABEL</a>$FOOTER_KURS_HTML
<a href="../">Kursübersicht</a>
</footer> </footer>
</body> </body>
</html> </html>
+73 -22
View File
@@ -9,6 +9,7 @@ title: Dateiformate, Schnittstellen, Speichermedien & Distributionswege
--- ---
<style> <style>
:root { :root {
--color-foreground: #1a1a2e; --color-foreground: #1a1a2e;
--color-highlight: #1e5f8a; --color-highlight: #1e5f8a;
@@ -75,11 +76,6 @@ section.erklaerung :not(footer)
{ {
font-size: 1.1rem; font-size: 1.1rem;
} }
@media print {
section.erklaerung {
background: #e3f2fd !important;
}
}
section.erklaerung h1 { section.erklaerung h1 {
font-size: 1.5rem; font-size: 1.5rem;
color: #1e5f8a; color: #1e5f8a;
@@ -353,7 +349,7 @@ dann **150 Mbit/s ÷ 8** = **18,75 MB/s**
**50 MB Upload bei 10 Mbit/s = ~40 Sekunden** **50 MB Upload bei 10 Mbit/s = ~40 Sekunden**
<!-- <!--
- ADSL (Asymmetric Digital Subscriber Line, ITU = International Telecommunication Union, Norm G.992.1, 1999): Upload bewusst begrenzt — Annahme war, Nutzer konsumieren mehr als sie produzieren - ADSL (Asymmetric Digital Subscriber Line, ITU = International Telecommunication Union, Norm G.992.1, 1999): Upload bewusst begrenzt — Annahme war, Nutzende konsumieren mehr als sie produzieren
- FTTH = Fiber To The Home (Glasfaser bis in die Wohnung) - FTTH = Fiber To The Home (Glasfaser bis in die Wohnung)
- Glasfaser (FTTH) symmetrisch: Upload = Download - Glasfaser (FTTH) symmetrisch: Upload = Download
- FTTH-Verfügbarkeit Deutschland Ende 2024: ~40% der Haushalte (Bundesnetzagentur, Breitbandatlas) - FTTH-Verfügbarkeit Deutschland Ende 2024: ~40% der Haushalte (Bundesnetzagentur, Breitbandatlas)
@@ -439,7 +435,7 @@ Jede Messung = ein Zahlenwert
<!-- <!--
- Dynamikumfang Formel: ~6 dB pro Bit - Dynamikumfang Formel: ~6 dB pro Bit
- 16 Bit reicht für menschliches Hören unter realen Bedingungen (~80–90 dB) - 16 Bit reicht für menschliches Hören unter realen Bedingungen (~80–90 dB)
- 24 Bit im Studio: mehr Headroom für Bearbeitung, kein perzipieller Mehrwert für Endnutzer (Meyer & Moran, JAES 2007) - 24 Bit im Studio: mehr Headroom für Bearbeitung, kein perzipieller Mehrwert für Endnutzende (Meyer & Moran, JAES 2007)
- dB = Dezibel: logarithmische Einheit für Lautstärke - dB = Dezibel: logarithmische Einheit für Lautstärke
--> -->
@@ -561,7 +557,7 @@ RECHNUNG:
- CD (1982, Sony/Philips): erstes massenmarktfähiges digitales Distributionsmedium für unkomprimiertes PCM-Audio - CD (1982, Sony/Philips): erstes massenmarktfähiges digitales Distributionsmedium für unkomprimiertes PCM-Audio
- PCM (Pulse-Code Modulation) existierte bereits vorher: NHK (Nippon Hōsō Kyōkai = japanische öffentlich-rechtliche Rundfunkanstalt)-Forschung ab 1960er, digitale Studiogeräte ab 1970er - PCM (Pulse-Code Modulation) existierte bereits vorher: NHK (Nippon Hōsō Kyōkai = japanische öffentlich-rechtliche Rundfunkanstalt)-Forschung ab 1960er, digitale Studiogeräte ab 1970er
- Die CD war nicht das erste digitale Audio — aber das erste das in Konsumentenhänden landete - Die CD war nicht das erste digitale Audio — aber das erste das in KonsumentInnen-Hand landete
--> -->
--- ---
@@ -600,11 +596,12 @@ RECHNUNG:
--- ---
<!-- _class: erklaerung <!--
_class: erklaerung
# CD-Audio – Vertiefung # CD-Audio – Vertiefung
Die CD (1982) war nicht das erste digitale Audiomedium — PCM-Aufnahmen existierten seit den späten 1960ern in Tonstudios (u.a. NHK, Nippon Columbia). Die CD war das erste **massenmarktfähige** digitale Distributionsmedium für unkomprimiertes Audio in Konsumentenhänden. Die CD (1982) war nicht das erste digitale Audiomedium — PCM-Aufnahmen existierten seit den späten 1960ern in Tonstudios (u.a. NHK, Nippon Columbia). Die CD war das erste **massenmarktfähige** digitale Distributionsmedium für unkomprimiertes Audio in KonsumentInnen-Hand.
**Warum 44.100 Hz?** **Warum 44.100 Hz?**
Kombination aus Nyquist-Limit (2× 20 kHz = 40 kHz Minimum) und Kompatibilität mit Videobandgeräten, die in frühen digitalen Tonstudios als Speichermedium genutzt wurden. NTSC: 3 Samples × 245 Zeilen × 60 Felder = 44.100. PAL: 3 Samples × 294 Zeilen × 50 Felder = 44.100. Kombination aus Nyquist-Limit (2× 20 kHz = 40 kHz Minimum) und Kompatibilität mit Videobandgeräten, die in frühen digitalen Tonstudios als Speichermedium genutzt wurden. NTSC: 3 Samples × 245 Zeilen × 60 Felder = 44.100. PAL: 3 Samples × 294 Zeilen × 50 Felder = 44.100.
@@ -847,7 +844,7 @@ KLAUSURRELEVANT:
<!-- <!--
--- ---
_class: erklaerung _class: erklaerung
# Kompression – Vertiefung # Kompression – Vertiefung
@@ -1226,6 +1223,58 @@ Sog. RGB Tuple (geordnete endliche Liste)
--- ---
# Wo begegnet Ihnen Hex-Code?
| Kontext | Beispiel |
|---------|----------|
| CSS-Farben | `#FF5733` |
| MAC-Adressen | `00:1A:2B:3C:4D:5E` |
| Fehlercodes | `0x80070005` |
| Speicheradressen | `0xA04F20` |
| Unicode | `U+00E4` (ä) |
| Datei-Signaturen | `89 50 4E 47` (PNG) |
<!--
Präfixe:
- 0x = "das ist Hexadezimal" (C, JavaScript, Python)
- U+ = Unicode-Codepoint (Standard für Zeichenkodierung)
- # = CSS-Konvention für Farben
ABKÜRZUNGEN:
- MAC = Media Access Control (eindeutige Hardware-Adresse einer Netzwerkkarte)
- Unicode = universeller Zeichensatz für alle Schriftsysteme
Speicheradressen erklärt:
- 64-Bit-System → Adresse hat 64 Bit = 16 Hex-Ziffern
- Beispiel vollständig: 0x0000000000A04F20 (führende Nullen weggelassen)
- "0x" = Präfix, sagt nur "jetzt kommt Hex"
Fehlercodes: Windows zeigt diese bei Bluescreens
-->
---
# Bit vs. Byte: Die Verwirrung
**1 Byte = 8 Bit** → Bit ÷ 8 = Byte
| Einheit (Bit) | Einheit (Byte) |
|---------------|----------------|
| 1 Kbit = 1.000 Bit | 1 KB = 1.000 Byte = 8.000 Bit |
| 1 Mbit = 1.000.000 Bit | 1 MB = 1.000.000 Byte = 8 Mbit |
| 1 Gbit = 1 Mrd. Bit | 1 GB = 1 Mrd. Byte = 8 Gbit |
100 Mbit/s Bandbreite = **12,5 MB/s** Downloadrate
<!--
Bits = kleines b, Bytes = großes B
Internetanbieter: Mbit/s (klingt größer!)
100 Mbit/s ÷ 8 = 12,5 MB/s
Marketing - 100 klingt besser als 12,5
-->
---
<!-- _class: lead --> <!-- _class: lead -->
# ASCII # ASCII
@@ -1691,7 +1740,7 @@ Fangfrage: "Wie hoch ist die Sample Rate von Vinyls?" -> Vinyl has no sample rat
<!-- <!--
- Dynamikumfang Formel: ~6 dB pro Bit - Dynamikumfang Formel: ~6 dB pro Bit
- 16 Bit reicht für menschliches Hören unter realen Bedingungen - 16 Bit reicht für menschliches Hören unter realen Bedingungen
- 24 Bit im Studio: mehr Headroom für Bearbeitung, kein perzipieller Mehrwert für Endnutzer (Meyer & Moran, JAES 2007) - 24 Bit im Studio: mehr Headroom für Bearbeitung, kein perzipieller Mehrwert für Endnutzende (Meyer & Moran, JAES 2007)
--> -->
--- ---
@@ -1896,7 +1945,7 @@ Ein Zusammenspiel aus vielen Faktoren:
**P2P-Filesharing für MP3s** **P2P-Filesharing für MP3s**
- Shawn Fanning, 19 Jahre alt - Shawn Fanning, 19 Jahre alt
- 80 Millionen User in 2 Jahren - 80 Millionen Nutzende in 2 Jahren
- Musikindustrie verklagt (2001) - Musikindustrie verklagt (2001)
- Pandora's Box: Nicht mehr aufzuhalten - Pandora's Box: Nicht mehr aufzuhalten
@@ -1913,7 +1962,7 @@ Ein Zusammenspiel aus vielen Faktoren:
# Napster & Musikindustrie # Napster & Musikindustrie
**1999:** Napster startet **1999:** Napster startet
**2001:** 80 Millionen User **2001:** 80 Millionen Nutzende
**Musikindustrie:** **Musikindustrie:**
- CDs kosten $15–20 - CDs kosten $15–20
@@ -2007,19 +2056,21 @@ Vollständige Lizenz: https://creativecommons.org/licenses/by-sa/4.0/
# Selbstlernen: HEX Files # Selbstlernen: HEX Files
1. Drei Dateien ohne Dateiendung: 1. Fünf Dateien ohne Dateiendung:
<a href="../materials/wtf1"/>`hex1`</a> <a href="../materials/wtf2"/>`hex2`</a> <a href="../materials/wtf3"/>`hex3`</a> <a href="https://librete.ch/hdm/223015b/materials/hex1">`hex1`</a> · <a href="https://librete.ch/hdm/223015b/materials/hex2">`hex2`</a> · <a href="https://librete.ch/hdm/223015b/materials/hex3">`hex3`</a> · <a href="https://librete.ch/hdm/223015b/materials/hex4">`hex4`</a> · <a href="https://librete.ch/hdm/223015b/materials/hex5">`hex5`</a>
3. Lies erste 16 Bytes aus und identifiziere Dateiformat (Magic Number) 2. Lies erste 16 Byte aus und identifiziere Dateiformat (Magic Number)
5. *Optional: Datei umbenennen und korrekte Dateiendung anhängen (bspw. `.jpg`)* 3. *Optional: Datei umbenennen und korrekte Dateiendung anhängen (bspw. `.jpg`)*
**Tools:** **Tools:**
- Hex-Editor: [hexed.it](https://hexed.it) - Hex-Editor: [hexed.it](https://hexed.it)
- Magic Numbers: [en.wikipedia.org/wiki/List_of_file_signatures](https://en.wikipedia.org/wiki/List_of_file_signatures) - Magic Numbers: [en.wikipedia.org/wiki/List_of_file_signatures](https://en.wikipedia.org/wiki/List_of_file_signatures)
<!-- <!--
- wtf1: Plaintext (keine Magic Number) - hex1: Plaintext (keine Magic Number)
- wtf2: PNG (89 50 4E 47) - hex2: PNG (89 50 4E 47)
- wtf3: JPEG (FF D8 FF) - hex3: JPEG (FF D8 FF)
- hex4: DOCX (50 4B 03 04 — ZIP-Container)
- hex5: ZIP (50 4B 03 04)
- Gruppenarbeit: 3-4 Personen - Gruppenarbeit: 3-4 Personen
- Ziel: Hex-Dump lesen lernen, Dateiformate verstehen - Ziel: Hex-Dump lesen lernen, Dateiformate verstehen
--> -->
+63 -79
View File
@@ -72,18 +72,6 @@ section.aufgabe footer {
} }
section.erklaerung { section.erklaerung {
font-size: 1.1rem; font-size: 1.1rem;
background: repeating-linear-gradient(
135deg,
#e3f2fd,
#e3f2fd 40px,
#fff 40px,
#fff 80px
) !important;
}
@media print {
section.erklaerung {
background: #e3f2fd !important;
}
} }
section.erklaerung h1 { section.erklaerung h1 {
font-size: 1.5rem; font-size: 1.5rem;
@@ -232,7 +220,7 @@ Jeder Pixel speichert einen RGB-Farbwert (3 Bytes).
**Aufbau:** Liste von Pixeln mit Farbwerten (2D-Array) **Aufbau:** Liste von Pixeln mit Farbwerten (2D-Array)
**Speicherbedarf (unkomprimiert):** **Speicherbedarf (unkomprimiert):**
Breite × Höhe × Farbtiefe (in Bytes) Breite × Höhe × Farbtiefe (in Byte)
**Beispiele:** JPEG, PNG, WebP **Beispiele:** JPEG, PNG, WebP
@@ -373,6 +361,15 @@ Vektorgrafiken speichern keine Pixel, sondern mathematische Beschreibungen: Koor
- Frage: "Warum verschickt euch der Grafiker das Logo als .ai oder .svg?" - Frage: "Warum verschickt euch der Grafiker das Logo als .ai oder .svg?"
--> -->
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/raster-vs-vektor.png)
--- ---
<!-- _class: lead --> <!-- _class: lead -->
@@ -386,6 +383,14 @@ Vektorgrafiken speichern keine Pixel, sondern mathematische Beschreibungen: Koor
- Wir nutzen seine Schwächen aus - Wir nutzen seine Schwächen aus
--> -->
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/lv-original-vs-fake.jpg)
--- ---
<!-- _class: klausur --> <!-- _class: klausur -->
@@ -510,6 +515,16 @@ Das Auge ist ein Tiefpassfilter: Hohe Frequenzen (feine Details) werden schwäch
- Interaktive Demo: https://claude.ai/public/artifacts/452e62f1-8525-442d-aa88-bf3795cab2c5 - Interaktive Demo: https://claude.ai/public/artifacts/452e62f1-8525-442d-aa88-bf3795cab2c5
--> -->
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/jpeg-pipeline.png)
--- ---
<!-- _class: klausur --> <!-- _class: klausur -->
@@ -519,7 +534,7 @@ Das Auge ist ein Tiefpassfilter: Hohe Frequenzen (feine Details) werden schwäch
![bg right:20%](./assets/Barn-yuv.png) ![bg right:20%](./assets/Barn-yuv.png)
# JPEG Schritt 1: Farbraumkonversion # JPEG Schritt 1: Farbraumkonvertierung
**RGB → Y'CbCr** **RGB → Y'CbCr**
@@ -542,11 +557,33 @@ KLAUSURRELEVANT:
--- ---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/ybcbr.webp)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/Barn-rgb.png)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/Barn-cmyk.png)
---
<!-- _class: erklaerung --> <!-- _class: erklaerung -->
<!-- _header: '' --> <!-- _header: '' -->
<!-- _footer: '' --> <!-- _footer: '' -->
# Farbraumkonversion – Vertiefung # Farbraumkonvertierung – Vertiefung
RGB→YCbCr nutzt die Biologie des menschlichen Auges: 120 Mio. Stäbchen (Helligkeit) vs. nur 6 Mio. Zapfen (Farbe) – ein 20:1-Verhältnis. Die Transformation erfolgt über eine lineare Matrix: RGB→YCbCr nutzt die Biologie des menschlichen Auges: 120 Mio. Stäbchen (Helligkeit) vs. nur 6 Mio. Zapfen (Farbe) – ein 20:1-Verhältnis. Die Transformation erfolgt über eine lineare Matrix:
@@ -558,7 +595,7 @@ Cr = 0.500·R − 0.419·G − 0.081·B + 128
Die Gewichtung (G dominiert mit 59%) entspricht der spektralen Empfindlichkeit des Auges bei Tageslicht. Y enthält alle Schärfeinformation; Cb/Cr können reduziert werden. Die Gewichtung (G dominiert mit 59%) entspricht der spektralen Empfindlichkeit des Auges bei Tageslicht. Y enthält alle Schärfeinformation; Cb/Cr können reduziert werden.
**Chroma Subsampling – Notation J:a:b** (bezogen auf 4×2 Pixel): **Chroma Subsampling – Notation `J:a:b`** (bezogen auf 4×2 Pixel):
| Schema | Farbdaten | Einsatz | | Schema | Farbdaten | Einsatz |
|--------|-----------|---------| |--------|-----------|---------|
@@ -725,11 +762,6 @@ KLAUSURRELEVANT:
--- ---
<!-- _class: klausur -->
<!-- _header: '' -->
<!-- _footer: '' -->
<!-- _backgroundColor: #e3f2fd -->
# JPEG Schritt 6: Huffman-Coding # JPEG Schritt 6: Huffman-Coding
**Verlustfreie Kompression der Restwerte** **Verlustfreie Kompression der Restwerte**
@@ -782,37 +814,10 @@ JPEG verwendet zwei Huffman-Tabellen: eine für DC-Koeffizienten (Durchschnittsw
--- ---
<!-- _class: klausur -->
<!-- _header: '' --> <!-- _header: '' -->
<!-- _footer: '' --> <!-- _footer: '' -->
<!-- _backgroundColor: #e3f2fd -->
<!--
# Huffman-Coding: Beispiel
**Originaltext:** `ABRACADABRA` (11 Zeichen × 8 Bit = 88 Bit) ![bg](./assets/demos/summary-kap02.png)
**Häufigkeitsanalyse:**
A=5, B=2, R=2, C=1, D=1
**Huffman-Baum → Codes:**
| Zeichen | Häufigkeit | Code |
|---------|------------|------|
| A | 5 | `0` |
| B | 2 | `10` |
| R | 2 | `110` |
| C | 1 | `1110` |
| D | 1 | `1111` |
**Codiert:** `0 10 110 0 1110 0 1111 0 10 110 0` = **23 Bit**
**Kompression:** 88 → 23 Bit = **74% gespart**
- Beispiel Schritt für Schritt durchrechnen
- Warum funktioniert's? A kommt 5× vor, bekommt kürzesten Code
- Präfix-Eigenschaft: Kein Code ist Anfang eines anderen → eindeutig dekodierbar
- Frage: "Was passiert, wenn alle Zeichen gleich häufig sind?" → Keine Ersparnis
- In JPEG: Nicht Buchstaben, sondern DCT-Koeffizienten werden so codiert
-->
--- ---
@@ -836,8 +841,6 @@ A=5, B=2, R=2, C=1, D=1
# Andere Bildformate # Andere Bildformate
## PNG, GIF, WebP, AVIF ## PNG, GIF, WebP, AVIF
15:33 Uhr weiter
<!-- <!--
- JPEG ist nicht für alles geeignet - JPEG ist nicht für alles geeignet
- Jetzt: Alternativen und ihre Stärken - Jetzt: Alternativen und ihre Stärken
@@ -960,7 +963,7 @@ KLAUSURRELEVANT:
<!-- <!--
- Kompatibilität schlägt oft Effizienz - Kompatibilität schlägt oft Effizienz
- TIFF für Archivierung: Unkomprimiert oder verlustfrei - TIFF für Archivierung: Unkomprimiert oder verlustfrei
- RAW für Fotografen: Maximale Bearbeitungsfreiheit - RAW für FotografInnen: Maximale Bearbeitungsfreiheit
- Frage: "Was passiert, wenn ihr ein PNG auf Instagram hochladet?" - Frage: "Was passiert, wenn ihr ein PNG auf Instagram hochladet?"
--> -->
@@ -996,8 +999,8 @@ KLAUSURRELEVANT:
- Instagram ist keine Kunstgalerie, sondern Massenplattform - Instagram ist keine Kunstgalerie, sondern Massenplattform
- Optimiert für Geschwindigkeit, nicht Qualität - Optimiert für Geschwindigkeit, nicht Qualität
- Facebook, Twitter, TikTok – alle machen das - Facebook, Twitter, TikTok – alle machen das
- Tipp für Fotografen: Portfolio auf eigener Website - Tipp für FotografInnen: Portfolio auf eigener Website
- Diskussion: Ist das fair? Trade-off Nutzer vs. Plattform - Diskussion: Ist das fair? Trade-off Nutzende vs. Plattform
--> -->
--- ---
@@ -1074,7 +1077,7 @@ KLAUSURRELEVANT:
**Codec** (Coder-Decoder) definiert den Kompressionsalgorithmus. Derselbe Container kann verschiedene Codecs enthalten – die Dateiendung verrät den Codec nicht. **Codec** (Coder-Decoder) definiert den Kompressionsalgorithmus. Derselbe Container kann verschiedene Codecs enthalten – die Dateiendung verrät den Codec nicht.
| Container | Entwickler | Typische Codecs | Besonderheit | | Container | Entwicklung | Typische Codecs | Besonderheit |
|-----------|------------|-----------------|--------------| |-----------|------------|-----------------|--------------|
| MP4 | ISO/MPEG | H.264, H.265, AAC | Web-Standard, DRM-fähig | | MP4 | ISO/MPEG | H.264, H.265, AAC | Web-Standard, DRM-fähig |
| MKV | Matroska | Alle | Beliebig viele Streams, Kapitel | | MKV | Matroska | Alle | Beliebig viele Streams, Kapitel |
@@ -1129,29 +1132,10 @@ KLAUSURRELEVANT:
--- ---
# Container + Codec = Video <!-- _header: '' -->
<!-- _footer: '' -->
``` ![bg fit](./assets/demos/container-vs-codec.png)
┌─────────────────────────────┐
│ Container (z.B. MP4) │
│ ┌────────────────────────┐ │
│ │ Video-Stream (H.264) │ │
│ ├────────────────────────┤ │
│ │ Audio-Stream (AAC) │ │
│ ├────────────────────────┤ │
│ │ Untertitel (SRT) │ │
│ ├────────────────────────┤ │
│ │ Metadaten │ │
│ └────────────────────────┘ │
└─────────────────────────────┘
```
<!--
- Container = Verpackung
- Codec = wie der Inhalt komprimiert ist
- Ein MP4 mit H.264 und ein MP4 mit H.265 haben dieselbe Endung
- Wichtig für Kompatibilität: Player muss Codec unterstützen
-->
--- ---
@@ -1338,7 +1322,7 @@ H.264 (2003) ermöglichte erst YouTube (2005), Netflix-Streaming (2007) und Blu-
**Hardware-Ubiquität:** Seit 2010 hat jedes Smartphone, jede GPU, jeder Smart-TV einen H.264-Hardware-Decoder. Encoding in Echtzeit braucht keine CPU – das ermöglichte erst mobiles Video-Streaming und Videotelefonie. **Hardware-Ubiquität:** Seit 2010 hat jedes Smartphone, jede GPU, jeder Smart-TV einen H.264-Hardware-Decoder. Encoding in Echtzeit braucht keine CPU – das ermöglichte erst mobiles Video-Streaming und Videotelefonie.
**Patent-Pool (MPEG-LA):** ~2.000 Patente von 30+ Unternehmen. Endnutzer-Streaming ist lizenzfrei; Hardware-Hersteller zahlen ~$0,20/Gerät. **Patent-Pool (MPEG-LA):** ~2.000 Patente von 30+ Unternehmen. Endnutzungs-Streaming ist lizenzfrei; Hardware-Hersteller zahlen ~$0,20/Gerät.
--- ---
@@ -1359,7 +1343,7 @@ H.264 (2003) ermöglichte erst YouTube (2005), Netflix-Streaming (2007) und Blu-
<!-- <!--
- MPEG-LA = Licensing Authority - MPEG-LA = Licensing Authority
- Firefox weigerte sich lange, H.264 zu unterstützen - Firefox weigerte sich lange, H.264 zu unterstützen
- "Free to View" gilt nur für Endnutzer-Streaming - "Free to View" gilt nur für Endnutzungs-Streaming
- Diskussion: Sollten Standards patent-frei sein? - Diskussion: Sollten Standards patent-frei sein?
--> -->
@@ -72,18 +72,6 @@ section.aufgabe footer {
} }
section.erklaerung { section.erklaerung {
font-size: 1.1rem; font-size: 1.1rem;
background: repeating-linear-gradient(
135deg,
#e3f2fd,
#e3f2fd 40px,
#fff 40px,
#fff 80px
) !important;
}
@media print {
section.erklaerung {
background: #e3f2fd !important;
}
} }
section.erklaerung h1 { section.erklaerung h1 {
font-size: 1.5rem; font-size: 1.5rem;
@@ -131,7 +119,7 @@ Hochschule der Medien Stuttgart
<!-- _class: lead --> <!-- _class: lead -->
# Kapitel 3 – 23.01.2026 # Kapitel 3
## Speichermedien & Schnittstellen ## Speichermedien & Schnittstellen
--- ---
@@ -166,7 +154,7 @@ Hersteller: Dezimal (SI-Präfix) - klingt mehr!
Betriebssysteme: Binär - aber zeigen "GB" statt "GiB" Betriebssysteme: Binär - aber zeigen "GB" statt "GiB"
Diskrepanz bei 1 TB: 1.000.000.000.000 vs. 1.099.511.627.776 Bytes Diskrepanz bei 1 TB: 1.000.000.000.000 vs. 1.099.511.627.776 Bytes
→ 10% Unterschied bei TB-Größen → 10% Unterschied bei TB-Größen
Verwirrung für Konsumenten seit Jahrzehnten Verwirrung für KonsumentInnen seit Jahrzehnten
ISO/IEC 80000-13: Definiert KiB, MiB, GiB, TiB (2008) ISO/IEC 80000-13: Definiert KiB, MiB, GiB, TiB (2008)
--> -->
@@ -381,8 +369,7 @@ Seitdem native Linux-Unterstützung.
**Features:** **Features:**
- Journaling (Crash-Sicherheit) - Journaling (Crash-Sicherheit)
- Dateirechte (ACLs) - Dateirechte (ACLs)
- Kompression, Verschlüsselung - Kompression, Verschlüsselung, große Dateien und Volumes
- Große Dateien und Volumes
**Nachteile:** **Nachteile:**
- Nur Windows schreibt nativ - Nur Windows schreibt nativ
@@ -604,7 +591,7 @@ Voll: Langsamstes Backup, schnellste Wiederherstellung
<!-- <!--
Time Machine: Stündliche Snapshots, APFS-Integration Time Machine: Stündliche Snapshots, APFS-Integration
Veeam: Enterprise-Level, kostenlose Endnutzer-Version Veeam: Enterprise-Level, kostenlose Privatversion
rsync: Unix-Klassiker, extrem effizient rsync: Unix-Klassiker, extrem effizient
Borg: Deduplizierung + Verschlüsselung Borg: Deduplizierung + Verschlüsselung
--> -->
@@ -641,11 +628,6 @@ Millionen von Websites und Spielen verloren.
- 30 Jahre Haltbarkeit - 30 Jahre Haltbarkeit
- Für Cold Storage ideal - Für Cold Storage ideal
**M-DISC:**
- Spezielle DVD/Blu-ray
- 1.000+ Jahre Haltbarkeit (Herstellerangabe)
- Für kleine, wichtige Daten
**Cloud:** **Cloud:**
- Glacier, Backblaze B2 - Glacier, Backblaze B2
- Günstig für Langzeit - Günstig für Langzeit
+14 -14
View File
@@ -160,7 +160,7 @@ AWS Snowmobile: 45-Fuß-Container auf LKW
**Prozess:** **Prozess:**
1. AWS schickt verschlüsseltes Gerät 1. AWS schickt verschlüsseltes Gerät
2. Kunde kopiert Daten lokal (schnell!) 2. KundIn kopiert Daten lokal (schnell!)
3. Gerät zurück an AWS 3. Gerät zurück an AWS
4. AWS lädt in S3 hoch 4. AWS lädt in S3 hoch
@@ -220,7 +220,7 @@ Server unter Last (symbolisch)
**Klassisches Modell:** Ein Server, viele Clients **Klassisches Modell:** Ein Server, viele Clients
**Problem:** **Problem:**
1 Million User wollen 1 GB-Datei 1 Million Nutzende wollen 1 GB-Datei
→ Server braucht **1 PB Bandbreite!** → Server braucht **1 PB Bandbreite!**
→ Server überlastet → **"Hug of Death"** → Server überlastet → **"Hug of Death"**
@@ -252,7 +252,7 @@ Globale Verteilung
**Funktionsweise:** **Funktionsweise:**
1. **Origin Server** (Hauptquelle) 1. **Origin Server** (Hauptquelle)
2. **Edge Servers** (geografisch verteilt) 2. **Edge Servers** (geografisch verteilt)
3. User → nächster Edge Server 3. Nutzende → nächster Edge Server
4. Erste Anfrage: Edge holt von Origin, cached 4. Erste Anfrage: Edge holt von Origin, cached
5. Weitere Anfragen: Direkt vom Edge (schnell!) 5. Weitere Anfragen: Direkt vom Edge (schnell!)
@@ -277,7 +277,7 @@ Große CDN-Anbieter: Cloudflare, Akamai, Amazon CloudFront, Fastly
- Lange Cache-Zeit (TTL: Tage/Wochen) - Lange Cache-Zeit (TTL: Tage/Wochen)
**Dynamic Content:** **Dynamic Content:**
- User-spezifisch (Profil) - Nutzungs-spezifisch (Profil)
- Kurze TTL oder nicht cachebar - Kurze TTL oder nicht cachebar
**Cache Invalidation:** **Cache Invalidation:**
@@ -349,14 +349,14 @@ Dezentral, jeder ist Client & Server
- IPFS (InterPlanetary File System) - IPFS (InterPlanetary File System)
- Blockchain (Bitcoin, Ethereum) - Blockchain (Bitcoin, Ethereum)
**Vorteil:** Skalierbar (mehr User = mehr Bandbreite!) **Vorteil:** Skalierbar (mehr Nutzende = mehr Bandbreite!)
**Nachteil:** Langsam bei wenigen Peers, oft für Piraterie missbraucht **Nachteil:** Langsam bei wenigen Peers, oft für Piraterie missbraucht
<!-- <!--
P2P vs. Client-Server: Kein zentraler Server P2P vs. Client-Server: Kein zentraler Server
Last verteilt sich auf alle Teilnehmer Last verteilt sich auf alle Teilnehmenden
Je mehr User, desto schneller (umgekehrt zu Client-Server!) Je mehr Nutzende, desto schneller (umgekehrt zu Client-Server!)
Zensur-resistent: Kein Single Point of Failure Zensur-resistent: Kein Single Point of Failure
Rechtliche Grauzone: Technologie neutral, aber oft illegal genutzt Rechtliche Grauzone: Technologie neutral, aber oft illegal genutzt
--> -->
@@ -1023,8 +1023,8 @@ Privacy-Albtraum
**Beispiele:** **Beispiele:**
- **Foto:** Kamera, Datum, GPS, Belichtung - **Foto:** Kamera, Datum, GPS, Belichtung
- **MP3:** Künstler, Album, Jahr, Genre, Cover - **MP3:** KünstlerInnen, Album, Jahr, Genre, Cover
- **PDF:** Autor, Datum, Software - **PDF:** AutorIn, Datum, Software
- **E-Mail:** Absender, Empfänger, Zeitstempel - **E-Mail:** Absender, Empfänger, Zeitstempel
**Warum wichtig?** **Warum wichtig?**
@@ -1180,7 +1180,7 @@ Wikipedia für Musik-Metadaten
**Idee:** Wikipedia für Musik-Metadaten **Idee:** Wikipedia für Musik-Metadaten
**Community-gepflegt:** **Community-gepflegt:**
- Künstler, Alben, Tracks - KünstlerInnen, Alben, Tracks
- Relationships (Band-Mitglieder, Label...) - Relationships (Band-Mitglieder, Label...)
- Releases (verschiedene Editionen, Länder) - Releases (verschiedene Editionen, Länder)
@@ -1239,7 +1239,7 @@ Versteckte Informationen sichtbar
**PDF-Metadaten (XMP):** **PDF-Metadaten (XMP):**
**Gespeichert:** **Gespeichert:**
- Titel, Autor, Betreff, Keywords - Titel, AutorIn, Betreff, Keywords
- Erstellungsdatum, Änderungsdatum - Erstellungsdatum, Änderungsdatum
- Software, **Company** (aus Office-Lizenz!) - Software, **Company** (aus Office-Lizenz!)
@@ -1600,7 +1600,7 @@ QKD: Abhör-sichere Kommunikation (Quantenmechanik garantiert)
**Filecoin (2017):** **Filecoin (2017):**
- Blockchain-basiert - Blockchain-basiert
- User vermieten Festplatten-Platz - Nutzende vermieten Festplatten-Platz
- Bezahlung in FIL (Kryptowährung) - Bezahlung in FIL (Kryptowährung)
**Arweave (2018):** **Arweave (2018):**
@@ -1638,7 +1638,7 @@ Legitime Use-Cases: NFT-Storage, Zensur-resistente Archivierung
- 5G + Edge Computing nötig - 5G + Edge Computing nötig
**Cloud Gaming:** **Cloud Gaming:**
- Spiel im Rechenzentrum, Stream zu User - Spiel im Rechenzentrum, Stream zu Nutzenden
- Input-Lag = Todfeind (<50ms) - Input-Lag = Todfeind (<50ms)
**Problem:** Physik (Lichtgeschwindigkeit!) **Problem:** Physik (Lichtgeschwindigkeit!)
@@ -1647,7 +1647,7 @@ Legitime Use-Cases: NFT-Storage, Zensur-resistente Archivierung
8K: YouTube unterstützt, aber wer hat 8K-Monitor? 8K: YouTube unterstützt, aber wer hat 8K-Monitor?
VR: Meta Quest, Apple Vision Pro VR: Meta Quest, Apple Vision Pro
Latenz: Lichtgeschwindigkeit ~300.000 km/s, aber Routing + Processing addiert sich Latenz: Lichtgeschwindigkeit ~300.000 km/s, aber Routing + Processing addiert sich
Edge Computing: Server näher an User (5G-Masten) Edge Computing: Server näher an Nutzende (5G-Masten)
Cloud Gaming: Stadia gescheitert (2023 †), GeForce Now, Xbox Cloud Cloud Gaming: Stadia gescheitert (2023 †), GeForce Now, Xbox Cloud
Input-Lag: Fiber ~5ms/1000km, aber Server-Processing + Encoding + Decoding Input-Lag: Fiber ~5ms/1000km, aber Server-Processing + Encoding + Decoding
--> -->
+34
View File
@@ -0,0 +1,34 @@
# CLAUDE.md — 223015b lecture deck
Marp lecture slides + master question catalog for HdM Stuttgart course **223015b — „Dateiformate, Schnittstellen, Speichermedien & Distributionswege"**. Author: Michael Czechowski, SoSe 2026. This directory is the **source of truth**; the exam artifacts (Moodle XML) are derived downstream (see [Downstream](#downstream)).
## Files
| File | Role |
|---|---|
| `00-intro.md` | Intro / organizational deck |
| `01-grundlagen-text-audio.md` | Grundlagen: Text & Audio |
| `02-bild-audio-video.md` | Bild, Audio, Video |
| `03-speichermedien-schnittstellen.md` | Speichermedien & Schnittstellen |
| `04-distribution-apis-zukunft.md` | Distribution, APIs, Zukunft |
| `05-vertiefung-offene-fragen.md` | Vertiefung / offene Fragen |
| `klausurfolien.md` | Exam-review slides |
| `klausurfragen.md` | **Master question catalog — 122 Fragen** (`[MC]/[MM]/[MATCH]/[ORDER]/[ESSAY]/[SHORTANS]/[NUMERIC]/[CLOZE]`) |
| `assets/`, `materials/` | Images and supporting material |
All decks are **Marp** Markdown (`marp: true`, theme `gaia`) with a shared inline `<style>` block. Highlight color for this course: `#1e5f8a` (blue). Sibling deck: `../223015c/` (Internettechnik, highlight `#d63384`).
## Conventions
- Each question in `klausurfragen.md` = `### <ID> – <title>` + `**Thema:**` + `**Punkte:**` + `**Typ:**` + body + `> **Feedback:**`/`> **Musterlösung:**`. Keep this shape — downstream tooling parses `**Thema:**` and `**Typ:**`.
- Correct MC options marked `- [x] **…** ✅`; matching as a 2-column table; ordering as a numbered list.
- `marp` CLI renders to HTML/PDF: `marp <file>.md` (see marp-team/marp-cli).
## Downstream
The **exam question bank + Moodle Quiz XML** live in a separate repo:
- Local: `/home/mwc/Documents/HDM/klausur/`
- Git: `ssh://tengo@git.librete.ch:41240/libretech/2026-hdm-klausur.git`
There, `klausurfragen-b-course-dateiformate.md` is a **curated subset (81/122)** of this deck's `klausurfragen.md`, and the questions are exported as Moodle XML for import. **Edit questions here first**, then sync the subset + regenerate/patch the XML downstream (that repo's CLAUDE.md + `moodle-quiz-xml` skill document the XML format and workflow).
Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

@@ -0,0 +1,205 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Aliasing — derselbe Fehler in zwei Sinnen</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--audio: #ea580c;
--bild: #2563eb;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 24px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.pair {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
max-width: 1200px;
margin: 0 auto;
}
.modality {
border: 2px solid;
border-radius: 12px;
padding: 16px 18px;
background: #fff;
display: flex;
flex-direction: column;
gap: 12px;
}
.modality.audio { border-color: var(--audio); }
.modality.bild { border-color: var(--bild); }
.mod-head { display: flex; justify-content: space-between; align-items: baseline; }
.mod-name { font-size: 1.25rem; font-weight: 700; }
.modality.audio .mod-name { color: var(--audio); }
.modality.bild .mod-name { color: var(--bild); }
.mod-tag { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.canvas {
background: var(--soft);
border-radius: 8px;
overflow: hidden;
}
.canvas svg { display: block; }
.scene-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.scene { display: flex; flex-direction: column; gap: 4px; }
.scene-label {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
}
.scene-cap {
font-size: 0.78rem;
color: var(--muted);
line-height: 1.4;
font-family: ui-monospace, monospace;
}
.scene-cap strong { color: var(--dark); }
.ear-eye {
font-size: 0.86rem;
line-height: 1.5;
color: var(--dark);
}
.ear-eye .arrow { color: var(--hl); font-weight: 700; }
.ear-eye-headline {
font-size: 0.9rem;
font-weight: 700;
}
.modality.audio .ear-eye-headline { color: var(--audio); }
.modality.bild .ear-eye-headline { color: var(--bild); }
.verdict {
margin-top: 22px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Aliasing — derselbe Fehler in zwei Sinnen</h1>
<div class="sub">Was im Ohr <strong>Brummen</strong> macht, macht im Auge <strong>Moiré</strong>. Wurzel: Sampling zu grob.</div>
<div class="pair">
<div class="modality audio">
<div class="mod-head">
<span class="mod-name">Audio</span>
<span class="mod-tag">Unterabtastung</span>
</div>
<div class="scene-pair">
<div class="scene">
<div class="scene-label">Original (18 kHz)</div>
<div class="canvas">
<svg viewBox="0 0 280 80" width="100%" height="80" preserveAspectRatio="none">
<line x1="0" y1="40" x2="280" y2="40" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,4"/>
<path d="M0,40 Q7,5 14,40 T28,40 T42,40 T56,40 T70,40 T84,40 T98,40 T112,40 T126,40 T140,40 T154,40 T168,40 T182,40 T196,40 T210,40 T224,40 T238,40 T252,40 T266,40 T280,40"
stroke="#ea580c" stroke-width="2" fill="none"/>
</svg>
</div>
<div class="scene-cap">20 Schwingungen<br>schnelle Welle</div>
</div>
<div class="scene">
<div class="scene-label">Bei 22 kHz gesampelt</div>
<div class="canvas">
<svg viewBox="0 0 280 80" width="100%" height="80" preserveAspectRatio="none">
<line x1="0" y1="40" x2="280" y2="40" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,4"/>
<path d="M0,40 Q7,5 14,40 T28,40 T42,40 T56,40 T70,40 T84,40 T98,40 T112,40 T126,40 T140,40 T154,40 T168,40 T182,40 T196,40 T210,40 T224,40 T238,40 T252,40 T266,40 T280,40"
stroke="#fed7aa" stroke-width="1.2" fill="none"/>
<path d="M0,40 Q70,10 140,40 T280,40"
stroke="#ea580c" stroke-width="2.5" fill="none"/>
</svg>
</div>
<div class="scene-cap"><strong>2 Schwingungen</strong><br>Brumm-Frequenz</div>
</div>
</div>
<div class="ear-eye">
<div class="ear-eye-headline">Was das Ohr hört</div>
Ein schriller 18-kHz-Ton wird zu einem dumpfen 4-kHz-Brumm. Das hohe ist <strong>nicht leiser</strong> — es ist <strong>verloren und falsch ersetzt</strong>.
</div>
</div>
<div class="modality bild">
<div class="mod-head">
<span class="mod-name">Bild</span>
<span class="mod-tag">Moiré-Effekt</span>
</div>
<div class="scene-pair">
<div class="scene">
<div class="scene-label">Original (feine Streifen)</div>
<div class="canvas">
<svg viewBox="0 0 280 80" width="100%" height="80" preserveAspectRatio="none">
<defs>
<pattern id="fine-real" x="0" y="0" width="3" height="80" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="1.5" height="80" fill="#1e3a8a"/>
</pattern>
</defs>
<rect x="0" y="0" width="280" height="80" fill="url(#fine-real)"/>
</svg>
</div>
<div class="scene-cap">93 Linien<br>volle Auflösung</div>
</div>
<div class="scene">
<div class="scene-label">In niedriger DPI gerendert</div>
<div class="canvas">
<svg viewBox="0 0 280 80" width="100%" height="80" preserveAspectRatio="none">
<defs>
<linearGradient id="moire-bands" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3a8a"/>
<stop offset="10%" stop-color="#bfdbfe"/>
<stop offset="20%" stop-color="#1e3a8a"/>
<stop offset="30%" stop-color="#bfdbfe"/>
<stop offset="40%" stop-color="#1e3a8a"/>
<stop offset="50%" stop-color="#bfdbfe"/>
<stop offset="60%" stop-color="#1e3a8a"/>
<stop offset="70%" stop-color="#bfdbfe"/>
<stop offset="80%" stop-color="#1e3a8a"/>
<stop offset="90%" stop-color="#bfdbfe"/>
<stop offset="100%" stop-color="#1e3a8a"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="280" height="80" fill="url(#moire-bands)"/>
</svg>
</div>
<div class="scene-cap"><strong>5 breite Bänder</strong><br>Moiré-Frequenz</div>
</div>
</div>
<div class="ear-eye">
<div class="ear-eye-headline">Was das Auge sieht</div>
Feine Linien sind weg, dafür <strong>breite Streifen</strong>, die nie da waren. Aus 93 Linien werden 5 Bänder — das Bild lügt.
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Aliasing kennt keine Modalität. Es ist ein <strong>mathematischer</strong> Fehler — kein technischer.</div>
<div class="verdict-a">Egal ob Ton, Bild, Video, Radar, GPS — Sampling muss immer mindestens <strong>doppelt</strong> so dicht sein wie das Signal. Sonst tauscht die Realität Frequenzen aus.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Byte für Byte — drei Brillen</title>
<style>
:root {
--hl: #d63384;
--dark: #1a1a2e;
--muted: #6b7280;
--border: #cbd5e1;
--bg-soft: #f8fafc;
--bin-bg: #0f0f23;
--bin-fg: #e5e7eb;
--binary-mark: #ef4444;
--ascii-ok: #16a34a;
--ctrl: #9ca3af;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 32px; }
h1 { text-align: center; margin: 0 0 4px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1rem; }
.sub code { font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.windows { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 18px; max-width: 1180px; margin: 0 auto; }
.window { border: 2px solid var(--border); border-radius: 10px; overflow: hidden; display: flex; flex-direction: column; }
.window-header { background: var(--bg-soft); padding: 10px 14px; border-bottom: 2px solid var(--border); display: flex; flex-direction: column; gap: 2px; }
.window-title { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
.window-name { font-size: 1.15rem; font-weight: 700; color: var(--dark); }
.window-body { padding: 14px 16px; flex: 1; display: flex; flex-direction: column; gap: 6px; font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.window.bin .window-body { background: var(--bin-bg); }
.window.bin .row { color: var(--bin-fg); font-size: 1.05rem; letter-spacing: 0.04em; }
.window.hex .row { color: var(--dark); font-size: 1.4rem; font-weight: 700; letter-spacing: 0.06em; }
.window.text .row { font-size: 1.4rem; font-weight: 700; display: flex; align-items: center; gap: 10px; }
.window.text .glyph.printable { color: var(--ascii-ok); }
.window.text .glyph.ctrl { color: var(--ctrl); font-size: 1.1rem; }
.window.text .glyph.binary { color: var(--binary-mark); }
.window.text .note { font-size: 0.78rem; font-weight: 400; color: var(--muted); margin-left: auto; }
.footnote { margin-top: 24px; max-width: 1100px; margin-left: auto; margin-right: auto; display: flex; gap: 32px; justify-content: center; font-size: 0.95rem; }
.legend { display: flex; align-items: center; gap: 8px; }
.swatch { display: inline-block; width: 16px; height: 16px; border-radius: 3px; }
.swatch.binary { background: #fee2e2; border: 2px solid var(--binary-mark); }
.swatch.ascii { background: #dcfce7; border: 2px solid var(--ascii-ok); }
.verdict { margin-top: 14px; text-align: center; font-size: 1.05rem; color: var(--dark); }
.verdict strong { color: var(--binary-mark); }
</style>
</head>
<body>
<h1>Byte für Byte</h1>
<div class="sub">Eine Datei — drei Brillen. Beispiel: erste 8 Byte einer PNG-Datei.</div>
<div class="windows">
<div class="window bin">
<div class="window-header">
<div class="window-title">Fenster 1</div>
<div class="window-name">Bin · rohe 0 und 1</div>
</div>
<div class="window-body">
<div class="row">10001001</div>
<div class="row">01010000</div>
<div class="row">01001110</div>
<div class="row">01000111</div>
<div class="row">00001101</div>
<div class="row">00001010</div>
<div class="row">00011010</div>
<div class="row">00001010</div>
</div>
</div>
<div class="window hex">
<div class="window-header">
<div class="window-title">Fenster 2</div>
<div class="window-name">Hex · 2 Ziffern pro Byte</div>
</div>
<div class="window-body">
<div class="row">89</div>
<div class="row">50</div>
<div class="row">4E</div>
<div class="row">47</div>
<div class="row">0D</div>
<div class="row">0A</div>
<div class="row">1A</div>
<div class="row">0A</div>
</div>
</div>
<div class="window text">
<div class="window-header">
<div class="window-title">Fenster 3</div>
<div class="window-name">Text · ASCII oder ✗ (> 126)</div>
</div>
<div class="window-body">
<div class="row"><span class="glyph binary">✗</span><span class="note">137 &gt; 126</span></div>
<div class="row"><span class="glyph printable">P</span><span class="note">80</span></div>
<div class="row"><span class="glyph printable">N</span><span class="note">78</span></div>
<div class="row"><span class="glyph printable">G</span><span class="note">71</span></div>
<div class="row"><span class="glyph ctrl">CR</span><span class="note">13 · Steuerz.</span></div>
<div class="row"><span class="glyph ctrl">LF</span><span class="note">10 · Steuerz.</span></div>
<div class="row"><span class="glyph ctrl">SUB</span><span class="note">26 · Steuerz.</span></div>
<div class="row"><span class="glyph ctrl">LF</span><span class="note">10 · Steuerz.</span></div>
</div>
</div>
</div>
<div class="verdict">
Mindestens ein Byte mit Wert &gt; 126 → <strong>Binärdatei</strong>. Sonst: reine Textdatei.
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Container ≠ Codec</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--container: #2563eb;
--video: #7c3aed;
--audio: #ea580c;
--subs: #16a34a;
--meta: #94a3b8;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.scene {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
max-width: 1200px;
margin: 0 auto;
}
.container-box {
border: 3px dashed var(--container);
border-radius: 14px;
padding: 14px 16px 18px;
background: #fff;
position: relative;
}
.container-tag {
position: absolute;
top: -12px;
left: 18px;
background: #fff;
padding: 0 10px;
font-family: ui-monospace, monospace;
font-size: 0.78rem;
font-weight: 700;
color: var(--container);
}
.container-label {
text-align: center;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
margin-bottom: 10px;
}
.track {
display: grid;
grid-template-columns: 90px 1fr;
gap: 12px;
align-items: center;
background: var(--soft);
border-left: 4px solid;
border-radius: 6px;
padding: 9px 14px;
margin-bottom: 6px;
}
.track.video { border-left-color: var(--video); }
.track.audio { border-left-color: var(--audio); }
.track.subs { border-left-color: var(--subs); }
.track.meta { border-left-color: var(--meta); }
.track-label {
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.track.video .track-label { color: var(--video); }
.track.audio .track-label { color: var(--audio); }
.track.subs .track-label { color: var(--subs); }
.track.meta .track-label { color: var(--meta); }
.codec-pill {
display: inline-block;
font-family: ui-monospace, monospace;
font-size: 0.82rem;
background: #fff;
border: 1px solid var(--border);
padding: 2px 9px;
border-radius: 4px;
color: var(--dark);
}
.verdict {
margin-top: 22px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
.verdict-a code { font-family: ui-monospace, monospace; background: var(--soft); padding: 1px 5px; border-radius: 4px; }
</style>
</head>
<body>
<h1>Container ≠ Codec</h1>
<div class="sub">Die <strong>Hülle</strong> sagt nichts darüber, <strong>wie</strong> der Inhalt komprimiert wurde.</div>
<div class="scene">
<div class="container-box">
<span class="container-tag">.mp4</span>
<div class="container-label">Datei A — älterer Stand</div>
<div class="track video">
<span class="track-label">Video</span>
<span><span class="codec-pill">H.264 / AVC</span> · 1080p · 8 Mbit/s</span>
</div>
<div class="track audio">
<span class="track-label">Audio</span>
<span><span class="codec-pill">AAC</span> · 192 kbit/s · Stereo</span>
</div>
<div class="track subs">
<span class="track-label">Untertitel</span>
<span><span class="codec-pill">TTML</span> · de + en</span>
</div>
<div class="track meta">
<span class="track-label">Metadaten</span>
<span>Titel · Dauer · Datum · Bitrate</span>
</div>
</div>
<div class="container-box">
<span class="container-tag">.mp4</span>
<div class="container-label">Datei B — modern</div>
<div class="track video">
<span class="track-label">Video</span>
<span><span class="codec-pill">AV1</span> · 4K · 25 Mbit/s</span>
</div>
<div class="track audio">
<span class="track-label">Audio</span>
<span><span class="codec-pill">Opus</span> · 128 kbit/s · 5.1 Surround</span>
</div>
<div class="track subs">
<span class="track-label">Untertitel</span>
<span><span class="codec-pill">WebVTT</span> · de + en + ja</span>
</div>
<div class="track meta">
<span class="track-label">Metadaten</span>
<span>Titel · Dauer · Datum · HDR-Info</span>
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Gleiche Endung. Anderes Innenleben.</div>
<div class="verdict-a"><strong>Container</strong> = Verpackung (<code>.mp4</code>, <code>.mkv</code>, <code>.webm</code>). <strong>Codec</strong> = Komprimierer (H.264 / AV1 / AAC / Opus). <strong>Tool:</strong> <code>mediainfo &lt;datei&gt;</code> zeigt beides.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Drei Dateien — dasselbe Innenleben</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--foto: #2563eb;
--audio: #ea580c;
--pdf: #7c3aed;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 32px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.85rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 28px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.files {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 22px;
max-width: 1200px;
margin: 0 auto;
}
.file {
border: 2px solid;
border-radius: 12px;
padding: 16px 18px;
background: #fff;
display: flex;
flex-direction: column;
gap: 12px;
}
.file.foto { border-color: var(--foto); }
.file.audio { border-color: var(--audio); }
.file.pdf { border-color: var(--pdf); }
.file-head { display: flex; justify-content: space-between; align-items: baseline; }
.file-name { font-size: 1.2rem; font-weight: 700; }
.file.foto .file-name { color: var(--foto); }
.file.audio .file-name { color: var(--audio); }
.file.pdf .file-name { color: var(--pdf); }
.file-ext { font-family: ui-monospace, monospace; font-size: 0.78rem; color: var(--muted); }
.preview {
border-radius: 8px;
border: 1px solid var(--border);
overflow: hidden;
display: block;
height: 130px;
background: var(--soft);
}
.hex-strip {
background: #0f0f23;
border-radius: 6px;
padding: 8px 10px;
font-family: ui-monospace, monospace;
font-size: 0.78rem;
line-height: 1.6;
color: #cbd5e1;
overflow: hidden;
}
.hex-strip .magic { color: #fff; font-weight: 700; padding: 0 2px; border-radius: 2px; }
.hex-strip.foto-magic .magic { background: var(--foto); }
.hex-strip.audio-magic .magic { background: var(--audio); }
.hex-strip.pdf-magic .magic { background: var(--pdf); }
.hex-label {
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
font-weight: 700;
margin-bottom: 2px;
}
.magic-caption {
font-size: 0.78rem;
color: var(--muted);
text-align: center;
font-family: ui-monospace, monospace;
}
.magic-caption .magic-word {
color: var(--dark);
font-weight: 700;
}
.verdict {
margin-top: 26px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.15rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.95rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Drei Dateien — dasselbe Innenleben</h1>
<div class="sub">Außen <strong>völlig verschieden</strong>. Innen reden alle dieselbe Sprache: <strong>Byte für Byte</strong>.</div>
<div class="files">
<div class="file foto">
<div class="file-head">
<span class="file-name">Foto</span>
<span class="file-ext">.png</span>
</div>
<svg class="preview" viewBox="0 0 220 130" preserveAspectRatio="xMidYMid slice">
<defs>
<linearGradient id="sky-f" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fde68a"/>
<stop offset="60%" stop-color="#fb923c"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="220" height="130" fill="url(#sky-f)"/>
<circle cx="160" cy="50" r="18" fill="#fef3c7" opacity="0.95"/>
<path d="M0,95 L40,80 L80,90 L120,72 L160,86 L220,75 L220,130 L0,130 Z" fill="#1e1b4b"/>
<path d="M0,110 L60,100 L120,108 L180,98 L220,105 L220,130 L0,130 Z" fill="#0f172a" opacity="0.7"/>
</svg>
<div>
<div class="hex-label">Erste Bytes der Datei</div>
<div class="hex-strip foto-magic">
<span class="magic">89 50 4E 47</span> 0D 0A 1A 0A 00 00 00 0D 49 48 44 52
</div>
</div>
<div class="magic-caption"><span class="magic-word">50 4E 47</span> = <span class="magic-word">"PNG"</span></div>
</div>
<div class="file audio">
<div class="file-head">
<span class="file-name">Sprachnachricht</span>
<span class="file-ext">.mp3</span>
</div>
<svg class="preview" viewBox="0 0 220 130" preserveAspectRatio="none">
<rect x="0" y="0" width="220" height="130" fill="#0f0f23"/>
<line x1="0" y1="65" x2="220" y2="65" stroke="#374151" stroke-width="1"/>
<path d="M0,65 L8,65 L12,55 L16,75 L20,40 L24,90 L28,30 L32,95 L36,35 L40,90
L48,45 L56,82 L64,50 L72,78 L80,52 L88,75 L96,55 L104,72
L116,58 L128,72 L140,60 L152,70 L168,62 L184,68 L200,64 L220,65
L220,130 L0,130 Z" fill="#fb923c" stroke="#ea580c" stroke-width="1"/>
</svg>
<div>
<div class="hex-label">Erste Bytes der Datei</div>
<div class="hex-strip audio-magic">
<span class="magic">49 44 33</span> 03 00 00 00 00 03 76 54 49 54 32 00 00
</div>
</div>
<div class="magic-caption"><span class="magic-word">49 44 33</span> = <span class="magic-word">"ID3"</span></div>
</div>
<div class="file pdf">
<div class="file-head">
<span class="file-name">PDF-Dokument</span>
<span class="file-ext">.pdf</span>
</div>
<svg class="preview" viewBox="0 0 220 130" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="220" height="130" fill="#f8fafc"/>
<rect x="50" y="14" width="120" height="102" rx="4" fill="#fff" stroke="#cbd5e1" stroke-width="1"/>
<rect x="58" y="24" width="80" height="4" rx="1" fill="#7c3aed"/>
<rect x="58" y="34" width="104" height="2" fill="#cbd5e1"/>
<rect x="58" y="40" width="98" height="2" fill="#cbd5e1"/>
<rect x="58" y="46" width="92" height="2" fill="#cbd5e1"/>
<rect x="58" y="52" width="100" height="2" fill="#cbd5e1"/>
<rect x="58" y="62" width="80" height="2" fill="#cbd5e1"/>
<rect x="58" y="68" width="96" height="2" fill="#cbd5e1"/>
<rect x="58" y="74" width="88" height="2" fill="#cbd5e1"/>
<rect x="58" y="84" width="40" height="3" rx="1" fill="#7c3aed" opacity="0.7"/>
<rect x="58" y="92" width="92" height="2" fill="#cbd5e1"/>
<rect x="58" y="98" width="86" height="2" fill="#cbd5e1"/>
<rect x="58" y="104" width="78" height="2" fill="#cbd5e1"/>
</svg>
<div>
<div class="hex-label">Erste Bytes der Datei</div>
<div class="hex-strip pdf-magic">
<span class="magic">25 50 44 46</span> 2D 31 2E 37 0A 25 E2 E3 CF D3 0A 0A
</div>
</div>
<div class="magic-caption"><span class="magic-word">25 50 44 46</span> = <span class="magic-word">"%PDF"</span></div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Drei verschiedene Dinge — eine Sprache.</div>
<div class="verdict-a">Jede Datei beginnt mit ihrer <strong>Visitenkarte</strong> (Magic Number) und besteht ab da aus nichts als Byte. Foto, Audio, PDF — alle dieselben Bauteile, nur anders zusammengesetzt.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>JPEG-Pipeline — 6 Schritte vom Foto zur Datei</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--lossless-step: #2563eb;
--lossy-step: #ea580c;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 26px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.pipeline {
display: grid;
grid-template-columns: 110px repeat(6, 1fr) 110px;
gap: 4px;
max-width: 1200px;
margin: 0 auto;
align-items: stretch;
}
.endpoint {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
text-align: center;
}
.endpoint-art { width: 88px; height: 60px; display: block; }
.endpoint-label {
font-size: 0.92rem;
font-weight: 700;
color: var(--dark);
}
.endpoint-size {
font-family: ui-monospace, monospace;
font-size: 0.82rem;
color: var(--muted);
}
.step {
border: 2px solid;
border-radius: 8px;
padding: 8px 6px;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
text-align: center;
}
.step.lossless { border-color: var(--lossless-step); }
.step.lossy { border-color: var(--lossy-step); }
.step-num {
font-family: ui-monospace, monospace;
font-size: 0.72rem;
font-weight: 700;
color: var(--muted);
}
.step-name {
font-size: 0.78rem;
font-weight: 700;
line-height: 1.2;
}
.step.lossless .step-name { color: var(--lossless-step); }
.step.lossy .step-name { color: var(--lossy-step); }
.step-desc {
font-size: 0.66rem;
color: var(--muted);
line-height: 1.3;
}
.arrow {
display: flex;
align-items: center;
justify-content: center;
color: var(--muted);
font-family: ui-monospace, monospace;
font-size: 1rem;
}
.legend {
margin-top: 16px;
display: flex;
justify-content: center;
gap: 24px;
font-size: 0.84rem;
color: var(--dark);
}
.legend-item { display: flex; align-items: center; gap: 6px; }
.legend-swatch {
width: 14px;
height: 14px;
border-radius: 3px;
display: inline-block;
}
.legend-swatch.lossless { background: var(--lossless-step); }
.legend-swatch.lossy { background: var(--lossy-step); }
.verdict {
margin-top: 22px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>JPEG-Pipeline — 6 Schritte vom Foto zur Datei</h1>
<div class="sub">Nur <strong>einer</strong> dieser Schritte verliert Information. Die anderen fünf sind verlustfrei.</div>
<div class="pipeline">
<div class="endpoint">
<svg class="endpoint-art" viewBox="0 0 88 60">
<defs>
<linearGradient id="photo-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fde68a"/>
<stop offset="55%" stop-color="#fb923c"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="88" height="60" rx="3" fill="url(#photo-grad)"/>
<circle cx="66" cy="22" r="9" fill="#fef3c7"/>
<path d="M0,42 L18,34 L40,40 L60,30 L80,38 L88,33 L88,60 L0,60 Z" fill="#1e1b4b"/>
</svg>
<div class="endpoint-label">Foto (Roh)</div>
<div class="endpoint-size">~36 MB</div>
</div>
<div class="step lossless">
<div class="step-num">1</div>
<div class="step-name">Farbraum</div>
<div class="step-desc">RGB → Y'CbCr<br>Helligkeit von Farbe trennen</div>
</div>
<div class="step lossy">
<div class="step-num">2</div>
<div class="step-name">Chroma-Subsampling</div>
<div class="step-desc">Cb &amp; Cr auf <strong>¼</strong><br>Auge sieht Farbe grob</div>
</div>
<div class="step lossless">
<div class="step-num">3</div>
<div class="step-name">8×8 Blöcke</div>
<div class="step-desc">Bild in Kacheln<br>jeder Block einzeln</div>
</div>
<div class="step lossless">
<div class="step-num">4</div>
<div class="step-name">DCT</div>
<div class="step-desc">Frequenz-Zerlegung<br>häufige Muster</div>
</div>
<div class="step lossy">
<div class="step-num">5</div>
<div class="step-name">Quantisierung</div>
<div class="step-desc"><strong>Hier</strong> wird weggeworfen<br>hohe Frequenzen raus</div>
</div>
<div class="step lossless">
<div class="step-num">6</div>
<div class="step-name">Huffman</div>
<div class="step-desc">Verlustfrei kürzen<br>häufig → kurz</div>
</div>
<div class="endpoint">
<svg class="endpoint-art" viewBox="0 0 88 60">
<rect x="0" y="0" width="88" height="60" rx="3" fill="url(#photo-grad)"/>
<circle cx="66" cy="22" r="9" fill="#fef3c7"/>
<path d="M0,42 L18,34 L40,40 L60,30 L80,38 L88,33 L88,60 L0,60 Z" fill="#1e1b4b"/>
</svg>
<div class="endpoint-label">JPEG</div>
<div class="endpoint-size">~3,5 MB</div>
</div>
</div>
<div class="legend">
<span class="legend-item"><span class="legend-swatch lossless"></span>verlustfrei (umkehrbar)</span>
<span class="legend-item"><span class="legend-swatch lossy"></span>verlustbehaftet (Information weg)</span>
</div>
<div class="verdict">
<div class="verdict-q">Der <strong>Quanten-Schritt</strong> ist das ganze Geheimnis von JPEG.</div>
<div class="verdict-a">Schritte 1, 3, 4, 6 verlieren nichts. Schritt 2 wirft Farb-Detail weg (Auge merkt nicht). Schritt 5 entscheidet — je nach Qualitäts-Stufe — wie viel <strong>Frequenz-Detail</strong> verloren geht. Niedrige Quality = aggressivere Schritt-5-Quantisierung.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Dieselbe Aufnahme, zwei Formate</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--lossless: #16a34a;
--lossy: #ef4444;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 36px 48px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.85rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 28px; font-size: 1.05rem; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; max-width: 1200px; margin: 0 auto; }
.panel { border: 2px solid var(--border); border-radius: 12px; padding: 22px 24px; display: flex; flex-direction: column; gap: 14px; background: #fff; }
.panel.left { border-color: var(--lossless); }
.panel.right { border-color: var(--lossy); }
.panel-head { display: flex; justify-content: space-between; align-items: baseline; }
.panel-tag {
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.panel-name { font-size: 1.5rem; font-weight: 700; }
.panel.left .panel-name { color: var(--lossless); }
.panel.right .panel-name { color: var(--lossy); }
.panel-format {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 0.85rem;
color: var(--muted);
}
.waveform {
background: #0f0f23;
border-radius: 8px;
padding: 6px;
display: block;
}
.stats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.stat { background: var(--soft); border: 1px solid var(--border); border-radius: 7px; padding: 8px 12px; }
.stat .label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); font-weight: 700; }
.stat .value { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 1rem; font-weight: 700; color: var(--dark); margin-top: 2px; }
.verdict {
margin-top: 26px;
border: 2px solid var(--hl);
background: #fff;
border-radius: 12px;
padding: 16px 22px;
text-align: center;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
.verdict-q { font-size: 1.2rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 1rem; color: var(--dark); margin-top: 6px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Dieselbe Aufnahme, zwei Formate</h1>
<div class="sub">3 Sekunden Snare-Hit mit Cymbal-Tail · 44,1 kHz Sample-Rate · Stereo</div>
<div class="grid">
<div class="panel left">
<div class="panel-head">
<span class="panel-name">Lossless</span>
<span class="panel-tag">FLAC</span>
</div>
<div class="panel-format">verlustfrei · alle Frequenzen 0–22 kHz erhalten</div>
<svg class="waveform" viewBox="0 0 800 160" preserveAspectRatio="none">
<defs>
<linearGradient id="green-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#34d399"/>
<stop offset="100%" stop-color="#16a34a"/>
</linearGradient>
</defs>
<line x1="0" y1="80" x2="800" y2="80" stroke="#374151" stroke-width="1"/>
<!-- detailed waveform: transient peak + cymbal decay with high-freq noise -->
<path d="M0,80
L40,80 L42,78 L44,82 L46,74 L48,88
L60,30 L62,135 L64,18 L66,142 L68,28 L70,128 L72,38 L74,118 L76,48 L78,108
L90,55 L92,108 L94,52 L96,112 L98,58 L100,105 L102,62 L104,98 L106,68 L108,92
L130,72 L132,92 L134,68 L136,94 L138,72 L140,90 L142,74 L144,88
L170,76 L172,86 L174,74 L176,87 L178,76 L180,85
L220,77 L222,84 L224,76 L226,85 L228,77 L230,83
L280,78 L282,83 L284,77 L286,84 L288,78 L290,82
L340,79 L342,82 L344,78 L346,82 L348,79 L350,81
L400,79 L402,81 L404,79 L406,81 L408,79 L410,81
L460,80 L462,81 L464,79 L466,81 L468,80 L470,80
L520,80 L522,81 L524,79 L526,80
L580,80 L582,80 L584,80
L640,80 L700,80 L800,80 Z"
fill="url(#green-grad)" stroke="#16a34a" stroke-width="1" opacity="0.95"/>
</svg>
<div class="stats">
<div class="stat"><div class="label">Dateigröße</div><div class="value">5,3 MB</div></div>
<div class="stat"><div class="label">Bitrate</div><div class="value">1.411 kbit/s</div></div>
</div>
</div>
<div class="panel right">
<div class="panel-head">
<span class="panel-name">Lossy</span>
<span class="panel-tag">MP3 128 kbit/s</span>
</div>
<div class="panel-format">verlustbehaftet · hohe Frequenzen weggeworfen, Transienten geglättet</div>
<svg class="waveform" viewBox="0 0 800 160" preserveAspectRatio="none">
<defs>
<linearGradient id="red-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fca5a5"/>
<stop offset="100%" stop-color="#ef4444"/>
</linearGradient>
</defs>
<line x1="0" y1="80" x2="800" y2="80" stroke="#374151" stroke-width="1"/>
<!-- smoothed waveform: same overall shape, no fine detail in transient peak -->
<path d="M0,80
L40,80 L48,80
L60,48 L65,120 L70,52 L75,115 L80,58 L85,108
L100,68 L108,92 L114,70 L120,90
L150,75 L160,86 L168,76 L174,85
L200,77 L210,84 L220,77 L230,83
L260,78 L280,82 L300,78 L320,82
L360,79 L380,81 L400,79 L420,81
L460,80 L480,80 L520,80
L580,80 L640,80 L700,80 L800,80 Z"
fill="url(#red-grad)" stroke="#ef4444" stroke-width="1" opacity="0.95"/>
</svg>
<div class="stats">
<div class="stat"><div class="label">Dateigröße</div><div class="value">0,48 MB</div></div>
<div class="stat"><div class="label">Bitrate</div><div class="value">128 kbit/s</div></div>
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Hört man den Unterschied?</div>
<div class="verdict-a">Auf dem Smartphone-Lautsprecher: meist nicht. Im Studio-Monitor mit Kopfhörer: oft schon. — MP3 wirft <strong>91 % der Daten weg</strong>, die der Mensch im Alltag nicht vermisst.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Drei Modalitäten, ein Hebel</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--bild: #2563eb;
--audio: #ea580c;
--video: #7c3aed;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 32px 48px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.85rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 26px; font-size: 1.05rem; }
.table { display: grid; grid-template-columns: 140px 1fr 1fr 1fr; gap: 12px; max-width: 1180px; margin: 0 auto; align-items: stretch; }
.col-head { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; font-weight: 700; color: var(--muted); padding: 0 8px 4px; }
.row-label { font-size: 1.15rem; font-weight: 700; padding: 16px 12px; display: flex; flex-direction: column; gap: 4px; justify-content: center; border-radius: 10px; }
.row-label.bild { background: #dbeafe; color: var(--bild); }
.row-label.audio { background: #ffedd5; color: var(--audio); }
.row-label.video { background: #ede9fe; color: var(--video); }
.row-label small { font-size: 0.78rem; font-weight: 400; color: var(--muted); text-transform: none; letter-spacing: 0; }
.cell { border: 1.5px solid var(--border); border-radius: 10px; padding: 12px 14px; background: #fff; display: flex; flex-direction: column; gap: 8px; }
.cell-format { font-family: ui-monospace, monospace; font-size: 0.85rem; color: var(--muted); }
.cell-size { font-size: 1.7rem; font-weight: 700; color: var(--dark); font-family: ui-monospace, monospace; }
.cell-bar { height: 8px; border-radius: 4px; background: var(--soft); position: relative; overflow: hidden; }
.cell-bar .fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 4px; }
.verdict {
margin-top: 22px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
text-align: center;
}
.verdict-q { font-size: 1.05rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.95rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Drei Modalitäten — derselbe Hebel</h1>
<div class="sub">Bild, Audio, Video schrumpfen alle dramatisch. Warum? Weil Wahrnehmung Lücken hat.</div>
<div class="table">
<div></div>
<div class="col-head">Roh</div>
<div class="col-head">Lossy (modern)</div>
<div class="col-head">Lossy (aggressiv)</div>
<div class="row-label bild">Bild<small>12-MP-Foto</small></div>
<div class="cell">
<div class="cell-format">RAW</div>
<div class="cell-size">36 MB</div>
<div class="cell-bar"><div class="fill" style="width:100%;background:#2563eb"></div></div>
</div>
<div class="cell">
<div class="cell-format">JPEG · Q 90</div>
<div class="cell-size">4 MB</div>
<div class="cell-bar"><div class="fill" style="width:11%;background:#2563eb"></div></div>
</div>
<div class="cell">
<div class="cell-format">JPEG · Q 50</div>
<div class="cell-size">800 KB</div>
<div class="cell-bar"><div class="fill" style="width:2.2%;background:#2563eb"></div></div>
</div>
<div class="row-label audio">Audio<small>30 s Stereo</small></div>
<div class="cell">
<div class="cell-format">WAV · 44,1 kHz · 16 bit</div>
<div class="cell-size">5,3 MB</div>
<div class="cell-bar"><div class="fill" style="width:100%;background:#ea580c"></div></div>
</div>
<div class="cell">
<div class="cell-format">MP3 · 192 kbit/s</div>
<div class="cell-size">720 KB</div>
<div class="cell-bar"><div class="fill" style="width:14%;background:#ea580c"></div></div>
</div>
<div class="cell">
<div class="cell-format">MP3 · 64 kbit/s</div>
<div class="cell-size">240 KB</div>
<div class="cell-bar"><div class="fill" style="width:4.5%;background:#ea580c"></div></div>
</div>
<div class="row-label video">Video<small>30 s · 4K · 30 fps</small></div>
<div class="cell">
<div class="cell-format">Roh · 8 bit</div>
<div class="cell-size">12 GB</div>
<div class="cell-bar"><div class="fill" style="width:100%;background:#7c3aed"></div></div>
</div>
<div class="cell">
<div class="cell-format">H.264 · 25 Mbit/s</div>
<div class="cell-size">94 MB</div>
<div class="cell-bar"><div class="fill" style="width:0.8%;background:#7c3aed"></div></div>
</div>
<div class="cell">
<div class="cell-format">H.265 · 12 Mbit/s</div>
<div class="cell-size">45 MB</div>
<div class="cell-bar"><div class="fill" style="width:0.4%;background:#7c3aed"></div></div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Bilder schrumpfen ×45. Audio ×22. Video ×270.</div>
<div class="verdict-a">Jede Reduktion nutzt eine andere Wahrnehmungs-Lücke: <strong>Bild</strong> die geringere Farbschärfe, <strong>Audio</strong> die Maskierung, <strong>Video</strong> die Frame-zu-Frame-Ähnlichkeit.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Die Reise einer Datei</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--slot1: #0891b2;
--slot2: #2563eb;
--slot3: #7c3aed;
--slot4: #be185d;
--slot5: #ea580c;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 32px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.85rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 30px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.journey {
display: grid;
grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr auto 1fr;
gap: 6px;
align-items: stretch;
max-width: 1200px;
margin: 0 auto;
}
.station {
border: 2px solid;
border-radius: 10px;
padding: 14px 12px 12px;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.station.s1 { border-color: var(--slot1); }
.station.s2 { border-color: var(--slot2); }
.station.s3 { border-color: var(--slot3); }
.station.s4 { border-color: var(--slot4); }
.station.s5 { border-color: var(--slot5); }
.station-tag { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.station-name { font-size: 1.05rem; font-weight: 700; text-align: center; line-height: 1.2; }
.station.s1 .station-name { color: var(--slot1); }
.station.s2 .station-name { color: var(--slot2); }
.station.s3 .station-name { color: var(--slot3); }
.station.s4 .station-name { color: var(--slot4); }
.station.s5 .station-name { color: var(--slot5); }
.station-detail { font-size: 0.78rem; color: var(--muted); text-align: center; }
.link {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 0 4px;
justify-content: center;
}
.link-rate {
font-family: ui-monospace, monospace;
font-size: 0.88rem;
font-weight: 700;
color: var(--dark);
white-space: nowrap;
}
.link-protocol {
font-size: 0.7rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.link-arrow svg { width: 32px; height: 14px; }
.scenario {
margin-top: 28px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
background: var(--soft);
border: 1.5px solid var(--border);
border-radius: 10px;
padding: 16px 22px;
}
.scenario-head { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); margin-bottom: 6px; }
.scenario-body { font-size: 0.98rem; line-height: 1.55; }
.scenario-body code { font-family: ui-monospace, monospace; background: #fff; padding: 1px 6px; border-radius: 4px; border: 1px solid var(--border); }
.verdict {
margin-top: 18px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.92rem; color: var(--dark); margin-top: 4px; }
</style>
</head>
<body>
<h1>Die Reise einer Datei</h1>
<div class="sub">Ein 4K-Trailer (<strong>1,2 GB</strong>) reist von der externen SSD auf den Monitor — jede Etappe ist ein eigener Engpass.</div>
<div class="journey">
<div class="station s1">
<div class="station-tag">Quelle</div>
<div class="station-name">SSD<br>extern</div>
<div class="station-detail">NVMe-Stick</div>
</div>
<div class="link">
<div class="link-rate">3 200 MB/s</div>
<svg class="link-arrow" viewBox="0 0 32 14"><path d="M2,7 L26,7 M22,2 L28,7 L22,12" stroke="#d63384" stroke-width="2" fill="none"/></svg>
<div class="link-protocol">USB 4 / TB4</div>
</div>
<div class="station s2">
<div class="station-tag">Aufnahme</div>
<div class="station-name">Laptop</div>
<div class="station-detail">RAM &amp; CPU</div>
</div>
<div class="link">
<div class="link-rate">~7 000 MB/s</div>
<svg class="link-arrow" viewBox="0 0 32 14"><path d="M2,7 L26,7 M22,2 L28,7 L22,12" stroke="#d63384" stroke-width="2" fill="none"/></svg>
<div class="link-protocol">PCIe 4.0</div>
</div>
<div class="station s3">
<div class="station-tag">Decoder</div>
<div class="station-name">GPU</div>
<div class="station-detail">H.264-Hardware</div>
</div>
<div class="link">
<div class="link-rate">2 200 MB/s</div>
<svg class="link-arrow" viewBox="0 0 32 14"><path d="M2,7 L26,7 M22,2 L28,7 L22,12" stroke="#d63384" stroke-width="2" fill="none"/></svg>
<div class="link-protocol">HDMI 2.1</div>
</div>
<div class="station s4">
<div class="station-tag">Display</div>
<div class="station-name">4K-Monitor</div>
<div class="station-detail">3840 × 2160</div>
</div>
<div class="link">
<div class="link-rate">visuell</div>
<svg class="link-arrow" viewBox="0 0 32 14"><path d="M2,7 L26,7 M22,2 L28,7 L22,12" stroke="#d63384" stroke-width="2" fill="none"/></svg>
<div class="link-protocol">Licht</div>
</div>
<div class="station s5">
<div class="station-tag">Ziel</div>
<div class="station-name">Auge</div>
<div class="station-detail">Bewusstsein</div>
</div>
</div>
<div class="scenario">
<div class="scenario-head">Was hier passiert</div>
<div class="scenario-body">
Die <code>1,2 GB</code> liegen verschlüsselt komprimiert auf dem Stick. Sie reisen über <strong>USB-C</strong> in den RAM, werden vom GPU <strong>dekodiert</strong> (H.264 → roher RGB-Strom), und über <strong>HDMI</strong> als unkomprimiertes Bild an den Monitor geschickt. Dort entstehen pro Sekunde 60 Bilder mit je rund <code>24 MB</code> — also <strong>1,4 GB/s an rohem Pixelstrom</strong>, der niemals als Datei vorliegt.
</div>
</div>
<div class="verdict">
<div class="verdict-q">Speicher und Schnittstelle sind keine zwei Themen — sie sind die zwei Seiten desselben Engpasses.</div>
<div class="verdict-a">Wo Bytes wohnen entscheidet, wie schnell sie reisen können. Wo sie reisen entscheidet, wo sie wohnen müssen.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Eine Datei reist — und erzählt</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--visible: #2563eb;
--hidden: #7c3aed;
--transit: #ea580c;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.85rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.scene {
display: grid;
grid-template-columns: 1fr 1.2fr;
gap: 22px;
max-width: 1200px;
margin: 0 auto;
}
.panel {
border: 2px solid;
border-radius: 12px;
padding: 16px 18px;
background: #fff;
display: flex;
flex-direction: column;
gap: 10px;
}
.panel.visible { border-color: var(--visible); }
.panel.hidden { border-color: var(--hidden); }
.panel-head { display: flex; justify-content: space-between; align-items: baseline; }
.panel-tag {
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.panel-name { font-size: 1.25rem; font-weight: 700; }
.panel.visible .panel-name { color: var(--visible); }
.panel.hidden .panel-name { color: var(--hidden); }
.photo {
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
display: block;
}
.photo-meta {
display: flex;
justify-content: space-between;
font-family: ui-monospace, monospace;
font-size: 0.78rem;
color: var(--muted);
padding: 0 2px;
}
.exif-list {
display: grid;
grid-template-columns: 130px 1fr;
gap: 4px 12px;
font-size: 0.86rem;
line-height: 1.4;
}
.exif-key {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--muted);
padding-top: 3px;
}
.exif-val { font-family: ui-monospace, monospace; color: var(--dark); }
.exif-val.gps { color: var(--hl); font-weight: 700; }
.transit-strip {
max-width: 1200px;
margin: 18px auto 0;
border: 2px solid var(--transit);
border-radius: 12px;
padding: 14px 18px;
background: #fff;
display: grid;
grid-template-columns: auto 1fr;
gap: 16px;
align-items: center;
}
.transit-tag {
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--transit);
writing-mode: vertical-rl;
transform: rotate(180deg);
text-align: center;
}
.transit-body { display: flex; flex-direction: column; gap: 8px; }
.transit-route {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.92rem;
font-weight: 700;
flex-wrap: wrap;
}
.hop {
background: var(--soft);
border: 1.5px solid var(--border);
padding: 4px 10px;
border-radius: 6px;
font-family: ui-monospace, monospace;
font-size: 0.82rem;
color: var(--dark);
}
.hop.origin { border-color: var(--visible); color: var(--visible); }
.hop.dest { border-color: var(--visible); color: var(--visible); }
.hop.edge { border-color: var(--transit); color: var(--transit); }
.arrow { color: var(--muted); font-family: ui-monospace, monospace; }
.transit-headers {
font-family: ui-monospace, monospace;
font-size: 0.78rem;
color: var(--muted);
line-height: 1.5;
}
.transit-headers strong { color: var(--dark); }
.verdict {
margin-top: 18px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Eine Datei reist — und erzählt</h1>
<div class="sub">Ein Smartphone-Foto geht von <strong>Stuttgart</strong> nach <strong>Tokyo</strong>. Was sieht der Empfänger — und was bekommt er noch dazu?</div>
<div class="scene">
<div class="panel visible">
<div class="panel-head">
<span class="panel-name">Inhalt</span>
<span class="panel-tag">Was du siehst</span>
</div>
<svg class="photo" viewBox="0 0 420 280" preserveAspectRatio="xMidYMid slice">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fde68a"/>
<stop offset="55%" stop-color="#fda4af"/>
<stop offset="100%" stop-color="#9333ea"/>
</linearGradient>
<linearGradient id="hills" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#312e81"/>
<stop offset="100%" stop-color="#0f172a"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="420" height="280" fill="url(#sky)"/>
<circle cx="320" cy="105" r="32" fill="#fff7ed" opacity="0.95"/>
<path d="M0,200 L60,170 L110,185 L160,160 L210,178 L260,150 L310,172 L360,148 L420,168 L420,280 L0,280 Z" fill="url(#hills)"/>
<path d="M0,225 L50,210 L100,222 L150,205 L200,220 L250,202 L300,218 L350,200 L420,215 L420,280 L0,280 Z" fill="#1e1b4b" opacity="0.7"/>
<rect x="80" y="225" width="14" height="42" fill="#0f172a"/>
<rect x="180" y="232" width="10" height="35" fill="#0f172a"/>
<rect x="240" y="220" width="18" height="48" fill="#0f172a"/>
<rect x="300" y="234" width="11" height="34" fill="#0f172a"/>
<rect x="86" y="232" width="3" height="3" fill="#fbbf24"/>
<rect x="188" y="240" width="2" height="2" fill="#fbbf24"/>
<rect x="248" y="228" width="3" height="3" fill="#fbbf24"/>
<rect x="307" y="240" width="2" height="2" fill="#fbbf24"/>
</svg>
<div class="photo-meta">
<span>HEIC · 4032 × 3024</span>
<span>3,2 MB</span>
</div>
</div>
<div class="panel hidden">
<div class="panel-head">
<span class="panel-name">Gepäck</span>
<span class="panel-tag">Was die Datei über sich weiß</span>
</div>
<div class="exif-list">
<div class="exif-key">Kamera</div>
<div class="exif-val">Apple iPhone 15 Pro · 24 mm · ƒ/1.78</div>
<div class="exif-key">Belichtung</div>
<div class="exif-val">1/250 s · ISO 100 · EV 0</div>
<div class="exif-key">Aufgenommen</div>
<div class="exif-val">2026-04-12 · 18:47:12 · +02:00</div>
<div class="exif-key">GPS</div>
<div class="exif-val gps">48.7847° N · 9.1825° E · 248 m<br><span style="font-weight:400">(Stuttgart, Königstraße 28)</span></div>
<div class="exif-key">Software</div>
<div class="exif-val">iOS 18.4.1 · HDR · Live Photo</div>
<div class="exif-key">Farbprofil</div>
<div class="exif-val">Display P3</div>
</div>
</div>
</div>
<div class="transit-strip">
<div class="transit-tag">Weg</div>
<div class="transit-body">
<div class="transit-route">
<span class="hop origin">Stuttgart</span>
<span class="arrow">→</span>
<span class="hop">DNS</span>
<span class="arrow">→</span>
<span class="hop">TLS 1.3</span>
<span class="arrow">→</span>
<span class="hop edge">CDN-Edge Tokyo</span>
<span class="arrow">→</span>
<span class="hop dest">Tokyo</span>
</div>
<div class="transit-headers">
<strong>HTTP/3 200 OK</strong> · content-type: image/heic · content-length: 3 220 480 · <strong>RTT 47 ms</strong> · cache: HIT (Edge tok-7) · age: 312 s
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Eine Datei = Inhalt + Gepäck + Weg.</div>
<div class="verdict-a"><strong>Inhalt</strong> sieht der Empfänger. <strong>Gepäck</strong> verrät, wer sie gemacht hat — und wo. <strong>Weg</strong> entscheidet, wie schnell sie ankommt. Drei Schichten, eine Datei.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+194
View File
@@ -0,0 +1,194 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>1 TB verkauft, 931 GB angezeigt — wo gehen die 69 GB hin?</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--marketing: #2563eb;
--os: #ea580c;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 32px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 24px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.compare {
display: grid;
grid-template-columns: 1fr 80px 1fr;
gap: 18px;
max-width: 1100px;
margin: 0 auto;
align-items: stretch;
}
.side {
border: 2px solid;
border-radius: 12px;
padding: 18px 22px;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.side.marketing { border-color: var(--marketing); }
.side.os { border-color: var(--os); }
.side-tag {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.side.marketing .side-name { color: var(--marketing); }
.side.os .side-name { color: var(--os); }
.side-name { font-size: 1.15rem; font-weight: 700; }
.label-mock {
background: var(--soft);
border: 1.5px solid var(--border);
border-radius: 8px;
padding: 14px 22px;
min-width: 240px;
text-align: center;
font-family: ui-monospace, monospace;
font-size: 0.85rem;
color: var(--muted);
line-height: 1.5;
}
.label-mock .big {
font-family: system-ui, sans-serif;
font-size: 2.2rem;
font-weight: 900;
display: block;
color: var(--dark);
line-height: 1.05;
}
.side.marketing .label-mock .big { color: var(--marketing); }
.side.os .label-mock .big { color: var(--os); }
.label-mock .small-line { font-size: 0.78rem; }
.formel {
font-family: ui-monospace, monospace;
font-size: 0.85rem;
color: var(--muted);
text-align: center;
line-height: 1.6;
}
.formel strong { color: var(--dark); }
.arrow-col {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
color: var(--muted);
}
.arrow-col svg { display: block; }
.arrow-col .why {
font-size: 0.78rem;
font-weight: 700;
color: var(--hl);
text-align: center;
line-height: 1.25;
}
.math-box {
margin-top: 22px;
max-width: 1100px;
margin-left: auto;
margin-right: auto;
background: var(--soft);
border: 1.5px solid var(--border);
border-radius: 10px;
padding: 16px 22px;
}
.math-box-head { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); margin-bottom: 8px; }
.math-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 22px; font-family: ui-monospace, monospace; font-size: 0.9rem; line-height: 1.6; }
.math-grid .key { color: var(--muted); }
.math-grid .val { color: var(--dark); font-weight: 700; }
.math-grid .val.hl { color: var(--hl); }
.verdict {
margin-top: 18px;
max-width: 1100px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.05rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a code { font-family: ui-monospace, monospace; background: #fff; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--border); }
</style>
</head>
<body>
<h1>„1 TB" gekauft — 931 GB im Finder. Wo sind die 69 GB hin?</h1>
<div class="sub">Nirgendwo. Es sind <strong>dieselben Byte</strong>. Nur zwei Welten zählen anders.</div>
<div class="compare">
<div class="side marketing">
<div class="side-tag">Auf der Verpackung</div>
<div class="side-name">Marketing</div>
<div class="label-mock">
<span class="big">1 TB</span>
<span class="small-line">externe SSD</span>
</div>
<div class="formel">
1 TB = <strong>10<sup>12</sup></strong> Byte<br>
= 1 000 000 000 000
</div>
</div>
<div class="arrow-col">
<svg viewBox="0 0 60 30" width="56" height="30"><path d="M5,15 L48,15 M42,7 L52,15 L42,23" stroke="#d63384" stroke-width="2.5" fill="none"/></svg>
<div class="why">geteilt durch<br>2<sup>30</sup></div>
</div>
<div class="side os">
<div class="side-tag">Im Finder · Explorer</div>
<div class="side-name">Betriebssystem</div>
<div class="label-mock">
<span class="big">931 GB</span>
<span class="small-line">eigentlich: 931 GiB</span>
</div>
<div class="formel">
1 GiB = <strong>2<sup>30</sup></strong> Byte<br>
= 1 073 741 824
</div>
</div>
</div>
<div class="math-box">
<div class="math-box-head">Die Rechnung</div>
<div class="math-grid">
<span class="key">Marketing schreibt</span>
<span class="val">1 TB = 1 000 000 000 000 Byte (Basis 10)</span>
<span class="key">Betriebssystem rechnet</span>
<span class="val">1 TiB = 1 099 511 627 776 Byte (Basis 2)</span>
<span class="key">1 TB in GiB umrechnen</span>
<span class="val hl">10<sup>12</sup> ÷ 2<sup>30</sup> ≈ 931,3 GiB — der Finder zeigt also „931 GB"</span>
<span class="key">Was die Studis sehen</span>
<span class="val">Etikett: GB · Anzeige: GB · Bedeutung: zwei verschiedene Dinge</span>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Es fehlt nichts. Es wird nur in zwei Sprachen gezählt.</div>
<div class="verdict-a">Korrekt wären <code>GiB</code> (binär, 1024) und <code>GB</code> (dezimal, 1000). In der Realität verwenden beide Welten <code>GB</code> und meinen verschiedene Zahlen.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Nyquist-Shannon — wie oft muss man messen?</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--bad: #ef4444;
--warn: #f59e0b;
--good: #16a34a;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.cases {
display: flex;
flex-direction: column;
gap: 12px;
max-width: 1180px;
margin: 0 auto;
}
.case {
display: grid;
grid-template-columns: 200px 1fr;
gap: 18px;
align-items: center;
border: 2px solid;
border-radius: 10px;
padding: 10px 14px;
background: #fff;
}
.case.bad { border-color: var(--bad); }
.case.warn { border-color: var(--warn); }
.case.good { border-color: var(--good); }
.case-meta { display: flex; flex-direction: column; gap: 4px; }
.case-tag {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.case-name { font-size: 1.05rem; font-weight: 700; }
.case.bad .case-name { color: var(--bad); }
.case.warn .case-name { color: var(--warn); }
.case.good .case-name { color: var(--good); }
.case-rate { font-family: ui-monospace, monospace; font-size: 0.82rem; color: var(--dark); }
.case-note { font-size: 0.78rem; color: var(--muted); line-height: 1.35; }
.plot { background: var(--soft); border-radius: 8px; }
.plot svg { display: block; }
.verdict {
margin-top: 20px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.05rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.92rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
.verdict-a code { font-family: ui-monospace, monospace; background: var(--soft); padding: 1px 5px; border-radius: 4px; }
</style>
</head>
<body>
<h1>Nyquist-Shannon — wie oft müssen wir messen?</h1>
<div class="sub">Dieselbe Welle, drei Sample-Raten. Eine fällt durch, eine reicht knapp, eine ist sicher.</div>
<div class="cases">
<div class="case bad">
<div class="case-meta">
<div class="case-tag">Zu wenig</div>
<div class="case-name">Aliasing</div>
<div class="case-rate">f<sub>s</sub> = 0,9 · f</div>
<div class="case-note">Die Sample-Punkte sitzen auf der Welle — aber verbunden sehen sie aus wie eine <strong>ganz andere, langsamere</strong> Welle.</div>
</div>
<div class="plot">
<svg viewBox="0 0 880 140" width="100%" height="140" preserveAspectRatio="none">
<line x1="20" y1="70" x2="860" y2="70" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,4"/>
<path d="M 20,70 Q 46.25,-20 72.5,70 T 125,70 T 177.5,70 T 230,70 T 282.5,70 T 335,70 T 387.5,70 T 440,70 T 492.5,70 T 545,70 T 597.5,70 T 650,70 T 702.5,70 T 755,70 T 807.5,70 T 860,70"
stroke="#cbd5e1" stroke-width="1.5" fill="none"/>
<polyline points="20,70 136.7,41 253.4,25.7 370.1,31.2 486.8,54.9 603.5,85.8 720.2,109.2 836.9,114.2"
stroke="#ef4444" stroke-width="2.5" fill="none"/>
<circle cx="20" cy="70" r="5" fill="#ef4444"/>
<circle cx="136.7" cy="41" r="5" fill="#ef4444"/>
<circle cx="253.4" cy="25.7" r="5" fill="#ef4444"/>
<circle cx="370.1" cy="31.2" r="5" fill="#ef4444"/>
<circle cx="486.8" cy="54.9" r="5" fill="#ef4444"/>
<circle cx="603.5" cy="85.8" r="5" fill="#ef4444"/>
<circle cx="720.2" cy="109.2" r="5" fill="#ef4444"/>
<circle cx="836.9" cy="114.2" r="5" fill="#ef4444"/>
</svg>
</div>
</div>
<div class="case warn">
<div class="case-meta">
<div class="case-tag">Genau</div>
<div class="case-name">Nyquist-Grenze</div>
<div class="case-rate">f<sub>s</sub> = 2 · f</div>
<div class="case-note">Sample exakt auf <strong>Peak und Tal</strong>. Mathematisch reicht das — visuell sieht es kaputt aus.</div>
</div>
<div class="plot">
<svg viewBox="0 0 880 140" width="100%" height="140" preserveAspectRatio="none">
<line x1="20" y1="70" x2="860" y2="70" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,4"/>
<path d="M 20,70 Q 46.25,-20 72.5,70 T 125,70 T 177.5,70 T 230,70 T 282.5,70 T 335,70 T 387.5,70 T 440,70 T 492.5,70 T 545,70 T 597.5,70 T 650,70 T 702.5,70 T 755,70 T 807.5,70 T 860,70"
stroke="#cbd5e1" stroke-width="1.5" fill="none"/>
<polyline points="46.25,25 98.75,115 151.25,25 203.75,115 256.25,25 308.75,115 361.25,25 413.75,115 466.25,25 518.75,115 571.25,25 623.75,115 676.25,25 728.75,115 781.25,25 833.75,115"
stroke="#f59e0b" stroke-width="2" fill="none" opacity="0.5"/>
<circle cx="46.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="98.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="151.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="203.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="256.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="308.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="361.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="413.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="466.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="518.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="571.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="623.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="676.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="728.75" cy="115" r="5" fill="#f59e0b"/>
<circle cx="781.25" cy="25" r="5" fill="#f59e0b"/>
<circle cx="833.75" cy="115" r="5" fill="#f59e0b"/>
</svg>
</div>
</div>
<div class="case good">
<div class="case-meta">
<div class="case-tag">Sicher</div>
<div class="case-name">Überabtastung</div>
<div class="case-rate">f<sub>s</sub> = 8 · f</div>
<div class="case-note">Genug Punkte — die Welle wird Punkt für Punkt nachgezeichnet. So macht es eine CD.</div>
</div>
<div class="plot">
<svg viewBox="0 0 880 140" width="100%" height="140" preserveAspectRatio="none">
<line x1="20" y1="70" x2="860" y2="70" stroke="#cbd5e1" stroke-width="1" stroke-dasharray="3,4"/>
<path d="M 20,70 Q 46.25,-20 72.5,70 T 125,70 T 177.5,70 T 230,70 T 282.5,70 T 335,70 T 387.5,70 T 440,70 T 492.5,70 T 545,70 T 597.5,70 T 650,70 T 702.5,70 T 755,70 T 807.5,70 T 860,70"
stroke="#16a34a" stroke-width="2.5" fill="none" opacity="0.9"/>
<circle cx="20" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="33.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="46.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="59.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="72.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="85.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="98.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="111.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="125" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="138.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="151.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="164.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="177.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="190.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="203.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="216.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="230" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="243.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="256.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="269.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="282.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="295.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="308.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="321.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="335" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="348.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="361.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="374.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="387.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="400.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="413.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="426.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="440" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="453.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="466.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="479.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="492.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="505.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="518.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="531.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="545" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="558.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="571.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="584.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="597.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="610.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="623.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="636.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="650" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="663.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="676.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="689.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="702.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="715.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="728.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="741.9" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="755" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="768.1" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="781.2" cy="25" r="2.5" fill="#16a34a"/>
<circle cx="794.4" cy="38.2" r="2.5" fill="#16a34a"/>
<circle cx="807.5" cy="70" r="2.5" fill="#16a34a"/>
<circle cx="820.6" cy="101.8" r="2.5" fill="#16a34a"/>
<circle cx="833.8" cy="115" r="2.5" fill="#16a34a"/>
<circle cx="846.9" cy="101.8" r="2.5" fill="#16a34a"/>
</svg>
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">f<sub>s</sub> ≥ 2 · f<sub>max</sub>. So einfach. So endgültig.</div>
<div class="verdict-a">Das Ohr hört bis <strong>20 kHz</strong>. 2 × 20 = 40 kHz. Die CD nimmt <code>44,1 kHz</code> — die 4,1 sind Sicherheits-Marge.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Wir haben gelernt zu lesen — jetzt: wie kommt es rein?</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--done: #94a3b8;
--next: #d63384;
--bytes: #0891b2;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 32px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.7rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 36px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.pivot {
display: grid;
grid-template-columns: 200px 1fr 240px 1fr 200px;
gap: 8px;
max-width: 1200px;
margin: 0 auto;
align-items: center;
}
.station {
border: 2px solid var(--border);
border-radius: 12px;
padding: 18px 14px;
background: #fff;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.station.bytes { border-color: var(--bytes); background: #0f0f23; color: #fff; }
.station-tag {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.station.bytes .station-tag { color: #94a3b8; }
.station-name { font-size: 1.3rem; font-weight: 700; }
.station.bytes .station-name { color: #67e8f9; }
.station-detail { font-size: 0.85rem; color: var(--muted); }
.station.bytes .station-detail { color: #94a3b8; font-family: ui-monospace, monospace; font-size: 0.78rem; line-height: 1.4; }
.station-art { display: flex; align-items: center; justify-content: center; height: 70px; }
.station-art svg { width: 80%; height: 100%; }
.strang {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 0 6px;
}
.strang-arrow svg { display: block; width: 100%; height: 28px; }
.strang-label {
font-size: 0.95rem;
font-weight: 700;
text-align: center;
line-height: 1.3;
}
.strang.next .strang-label { color: var(--next); }
.strang.done .strang-label { color: var(--done); text-decoration: line-through; text-decoration-color: var(--done); text-decoration-thickness: 1.5px; }
.strang-tag {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
text-align: center;
}
.strang.next .strang-tag { color: var(--next); }
.strang.done .strang-tag { color: var(--done); }
.verdict {
margin-top: 28px;
max-width: 1100px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.95rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Wir haben gelernt zu <strong>lesen</strong>. Jetzt: wie kommt es <strong>rein</strong>?</h1>
<div class="sub">Die rechte Seite haben wir abgearbeitet. Die linke ist die nächste Stunde.</div>
<div class="pivot">
<div class="station">
<div class="station-tag">Quelle</div>
<div class="station-name">Welt</div>
<div class="station-art">
<svg viewBox="0 0 120 70" preserveAspectRatio="xMidYMid meet">
<path d="M5,35 Q15,10 25,35 T45,35 T65,35 T85,35 T105,35 T115,35"
stroke="#d63384" stroke-width="2.5" fill="none"/>
<circle cx="115" cy="35" r="3" fill="#d63384"/>
</svg>
</div>
<div class="station-detail">Schallwelle · Lichtwelle<br>kontinuierlich · analog</div>
</div>
<div class="strang next">
<div class="strang-tag">jetzt dran</div>
<div class="strang-arrow">
<svg viewBox="0 0 220 28" preserveAspectRatio="none">
<line x1="6" y1="14" x2="200" y2="14" stroke="#d63384" stroke-width="2.5"/>
<path d="M195,7 L208,14 L195,21" stroke="#d63384" stroke-width="2.5" fill="none"/>
</svg>
</div>
<div class="strang-label">Sampling + Quantisierung</div>
</div>
<div class="station bytes">
<div class="station-tag">Mitte</div>
<div class="station-name">Datei</div>
<div class="station-detail">
89 50 4E 47<br>
0D 0A 1A 0A<br>
00 00 00 0D
</div>
</div>
<div class="strang done">
<div class="strang-tag">schon durch</div>
<div class="strang-arrow">
<svg viewBox="0 0 220 28" preserveAspectRatio="none">
<line x1="6" y1="14" x2="200" y2="14" stroke="#94a3b8" stroke-width="2"/>
<path d="M195,7 L208,14 L195,21" stroke="#94a3b8" stroke-width="2" fill="none"/>
</svg>
</div>
<div class="strang-label">Notation · Encoding · Format</div>
</div>
<div class="station">
<div class="station-tag">Ziel</div>
<div class="station-name">Mensch</div>
<div class="station-art">
<svg viewBox="0 0 120 70" preserveAspectRatio="xMidYMid meet">
<rect x="14" y="14" width="92" height="14" rx="2" fill="#1a1a2e"/>
<rect x="14" y="34" width="68" height="6" rx="1" fill="#cbd5e1"/>
<rect x="14" y="44" width="78" height="6" rx="1" fill="#cbd5e1"/>
<rect x="14" y="54" width="56" height="6" rx="1" fill="#cbd5e1"/>
</svg>
</div>
<div class="station-detail">Text · Bild · Klang<br>Bedeutung</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Die Datei ist die Mitte. Beide Wege führen durch sie hindurch.</div>
<div class="verdict-a"><strong>Rechts:</strong> aus Byte wird Bedeutung — gerade gelernt. &nbsp;&nbsp; <strong>Links:</strong> aus Welt wird Byte — jetzt.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Psychoakustik — was MP3 weglässt</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--kept: #16a34a;
--tossed: #94a3b8;
--masker: #2563eb;
--threshold: #ea580c;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.chart-wrap {
max-width: 1180px;
margin: 0 auto;
background: #fff;
border: 1.5px solid var(--border);
border-radius: 12px;
padding: 16px 20px;
}
.chart-head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 8px;
}
.chart-title { font-size: 1rem; font-weight: 700; }
.legend {
display: flex;
gap: 16px;
font-size: 0.78rem;
color: var(--muted);
}
.legend-item { display: flex; align-items: center; gap: 4px; }
.legend-swatch {
width: 14px;
height: 14px;
border-radius: 3px;
display: inline-block;
}
.legend-swatch.kept { background: var(--kept); }
.legend-swatch.tossed { background: var(--tossed); }
.legend-swatch.masker { background: var(--masker); }
.legend-swatch.threshold {
background: transparent;
border-top: 2px dashed var(--threshold);
height: 8px;
margin-top: 5px;
}
.verdict {
margin-top: 22px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Psychoakustik — was MP3 wegwirft</h1>
<div class="sub">Ein lauter Ton macht leise Nachbarn <strong>unhörbar</strong>. Was nicht hörbar ist, muss man nicht speichern.</div>
<div class="chart-wrap">
<div class="chart-head">
<div class="chart-title">Lautstärke (dB SPL) über Frequenz (Hz, logarithmisch)</div>
<div class="legend">
<span class="legend-item"><span class="legend-swatch masker"></span>lauter Ton (Masker)</span>
<span class="legend-item"><span class="legend-swatch kept"></span>hörbar · bleibt</span>
<span class="legend-item"><span class="legend-swatch tossed"></span>maskiert · raus</span>
<span class="legend-item"><span class="legend-swatch threshold"></span>Hörschwelle</span>
</div>
</div>
<svg viewBox="0 0 1100 380" width="100%" height="380">
<line x1="80" y1="340" x2="1060" y2="340" stroke="#1a1a2e" stroke-width="1.5"/>
<line x1="80" y1="20" x2="80" y2="340" stroke="#1a1a2e" stroke-width="1.5"/>
<text x="20" y="30" font-size="11" fill="#6b7280">dB</text>
<text x="40" y="50" font-size="10" fill="#6b7280" text-anchor="end">100</text>
<text x="40" y="125" font-size="10" fill="#6b7280" text-anchor="end">75</text>
<text x="40" y="200" font-size="10" fill="#6b7280" text-anchor="end">50</text>
<text x="40" y="275" font-size="10" fill="#6b7280" text-anchor="end">25</text>
<text x="40" y="345" font-size="10" fill="#6b7280" text-anchor="end">0</text>
<line x1="75" y1="50" x2="80" y2="50" stroke="#1a1a2e"/>
<line x1="75" y1="125" x2="80" y2="125" stroke="#1a1a2e"/>
<line x1="75" y1="200" x2="80" y2="200" stroke="#1a1a2e"/>
<line x1="75" y1="275" x2="80" y2="275" stroke="#1a1a2e"/>
<text x="100" y="360" font-size="10" fill="#6b7280">20</text>
<text x="260" y="360" font-size="10" fill="#6b7280">100</text>
<text x="420" y="360" font-size="10" fill="#6b7280">500</text>
<text x="580" y="360" font-size="10" fill="#6b7280">1k</text>
<text x="740" y="360" font-size="10" fill="#6b7280">5k</text>
<text x="900" y="360" font-size="10" fill="#6b7280">10k</text>
<text x="1040" y="360" font-size="10" fill="#6b7280">20k</text>
<text x="540" y="378" font-size="11" fill="#6b7280" text-anchor="middle">Frequenz [Hz]</text>
<path d="M100,80 Q170,130 260,200 Q350,290 420,310 Q500,335 580,330 Q670,310 740,295 Q820,280 900,250 Q970,200 1040,80"
stroke="#ea580c" stroke-width="2" stroke-dasharray="5,5" fill="none"/>
<path d="M460,340 L460,210 Q480,165 580,80 Q680,165 700,210 L700,340 Z"
fill="#2563eb" opacity="0.15"/>
<path d="M460,340 L460,210 Q480,165 580,80 Q680,165 700,210 L700,340"
stroke="#2563eb" stroke-width="2" fill="none" stroke-dasharray="2,3"/>
<rect x="572" y="80" width="16" height="260" fill="#2563eb"/>
<circle cx="240" cy="270" r="6" fill="#16a34a"/>
<text x="240" y="260" font-size="10" fill="#16a34a" text-anchor="middle" font-weight="700">bleibt</text>
<circle cx="420" cy="240" r="6" fill="#16a34a"/>
<text x="420" y="230" font-size="10" fill="#16a34a" text-anchor="middle" font-weight="700">bleibt</text>
<circle cx="510" cy="180" r="6" fill="#94a3b8"/>
<text x="510" y="170" font-size="10" fill="#94a3b8" text-anchor="middle">raus</text>
<circle cx="660" cy="200" r="6" fill="#94a3b8"/>
<text x="660" y="190" font-size="10" fill="#94a3b8" text-anchor="middle">raus</text>
<circle cx="640" cy="155" r="6" fill="#94a3b8"/>
<text x="640" y="145" font-size="10" fill="#94a3b8" text-anchor="middle">raus</text>
<circle cx="860" cy="220" r="6" fill="#16a34a"/>
<text x="860" y="210" font-size="10" fill="#16a34a" text-anchor="middle" font-weight="700">bleibt</text>
<circle cx="960" cy="170" r="6" fill="#16a34a"/>
<text x="960" y="160" font-size="10" fill="#16a34a" text-anchor="middle" font-weight="700">bleibt</text>
<text x="595" y="76" font-size="11" fill="#2563eb" font-weight="700">1 kHz · 90 dB · "Masker"</text>
<text x="780" y="105" font-size="11" fill="#ea580c" font-weight="700">Hörschwelle (in Ruhe)</text>
<text x="540" y="160" font-size="11" fill="#2563eb" font-weight="700" text-anchor="middle">Maskierungs-Bereich</text>
</svg>
</div>
<div class="verdict">
<div class="verdict-q">MP3 kodiert nur, was über der <strong>kombinierten Schwelle</strong> liegt.</div>
<div class="verdict-a">Alles unterhalb wird weggeworfen — das Ohr hört es ohnehin nicht. So spart MP3 typischerweise <strong>~90 % der Daten</strong>, ohne dass der Mensch im Alltag etwas vermisst.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

@@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Psychovisualität — Luminanz vor Chrominanz</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--full: #2563eb;
--reduced: #7c3aed;
--y: #1a1a2e;
--cb: #2563eb;
--cr: #ef4444;
--bar-bg: #e2e8f0;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 24px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.compare {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
max-width: 1180px;
margin: 0 auto;
}
.col {
border: 2px solid;
border-radius: 12px;
padding: 16px 20px;
background: #fff;
display: flex;
flex-direction: column;
gap: 12px;
}
.col.full { border-color: var(--full); }
.col.reduced { border-color: var(--reduced); }
.col-head { display: flex; justify-content: space-between; align-items: baseline; }
.col-tag {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.col-name { font-size: 1.25rem; font-weight: 700; }
.col.full .col-name { color: var(--full); }
.col.reduced .col-name { color: var(--reduced); }
.col-format {
font-family: ui-monospace, monospace;
font-size: 0.82rem;
color: var(--muted);
}
.preview {
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
}
.preview svg { display: block; }
.channels {
display: flex;
flex-direction: column;
gap: 6px;
}
.channel { display: grid; grid-template-columns: 90px 1fr 60px; gap: 8px; align-items: center; font-size: 0.85rem; }
.channel-label {
font-family: ui-monospace, monospace;
font-weight: 700;
}
.channel-bar { height: 14px; background: var(--bar-bg); border-radius: 3px; overflow: hidden; }
.channel-bar .fill { height: 100%; border-radius: 3px; }
.channel-bar .fill.y { background: var(--y); }
.channel-bar .fill.cb { background: var(--cb); }
.channel-bar .fill.cr { background: var(--cr); }
.channel-pct {
font-family: ui-monospace, monospace;
font-size: 0.78rem;
color: var(--muted);
text-align: right;
}
.summary-line {
display: flex;
justify-content: space-between;
background: var(--soft);
border-radius: 6px;
padding: 6px 12px;
font-family: ui-monospace, monospace;
font-size: 0.85rem;
color: var(--dark);
}
.summary-line strong { color: var(--hl); }
.verdict {
margin-top: 22px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Psychovisualität — Luminanz vor Chrominanz</h1>
<div class="sub">Das Auge sieht <strong>Helligkeit</strong> scharf. <strong>Farbe</strong> darf grob bleiben — und das Bild sieht trotzdem identisch aus.</div>
<div class="compare">
<div class="col full">
<div class="col-head">
<span class="col-name">Ohne Subsampling</span>
<span class="col-tag">4:4:4</span>
</div>
<div class="col-format">jedes Pixel speichert Y · Cb · Cr in voller Auflösung</div>
<svg class="preview" viewBox="0 0 320 130" preserveAspectRatio="xMidYMid slice">
<defs>
<linearGradient id="full-sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fde68a"/>
<stop offset="55%" stop-color="#fb923c"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="320" height="130" fill="url(#full-sky)"/>
<circle cx="230" cy="50" r="22" fill="#fef3c7"/>
<path d="M0,90 L40,75 L90,87 L140,68 L190,82 L240,66 L290,80 L320,72 L320,130 L0,130 Z" fill="#1e1b4b"/>
<path d="M0,108 L60,100 L120,108 L180,98 L240,106 L300,98 L320,103 L320,130 L0,130 Z" fill="#0f172a" opacity="0.7"/>
</svg>
<div class="channels">
<div class="channel">
<span class="channel-label">Y</span>
<div class="channel-bar"><div class="fill y" style="width:100%"></div></div>
<span class="channel-pct">100 %</span>
</div>
<div class="channel">
<span class="channel-label">Cb</span>
<div class="channel-bar"><div class="fill cb" style="width:100%"></div></div>
<span class="channel-pct">100 %</span>
</div>
<div class="channel">
<span class="channel-label">Cr</span>
<div class="channel-bar"><div class="fill cr" style="width:100%"></div></div>
<span class="channel-pct">100 %</span>
</div>
</div>
<div class="summary-line">
<span>Datenmenge:</span>
<span><strong>100 %</strong></span>
</div>
</div>
<div class="col reduced">
<div class="col-head">
<span class="col-name">JPEG · H.264 Standard</span>
<span class="col-tag">4:2:0</span>
</div>
<div class="col-format">Y voll · Cb &amp; Cr nur in 2×2-Pixel-Blöcken</div>
<svg class="preview" viewBox="0 0 320 130" preserveAspectRatio="xMidYMid slice">
<defs>
<linearGradient id="red-sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fde68a"/>
<stop offset="55%" stop-color="#fb923c"/>
<stop offset="100%" stop-color="#7c3aed"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="320" height="130" fill="url(#red-sky)"/>
<circle cx="230" cy="50" r="22" fill="#fef3c7"/>
<path d="M0,90 L40,75 L90,87 L140,68 L190,82 L240,66 L290,80 L320,72 L320,130 L0,130 Z" fill="#1e1b4b"/>
<path d="M0,108 L60,100 L120,108 L180,98 L240,106 L300,98 L320,103 L320,130 L0,130 Z" fill="#0f172a" opacity="0.7"/>
</svg>
<div class="channels">
<div class="channel">
<span class="channel-label">Y</span>
<div class="channel-bar"><div class="fill y" style="width:100%"></div></div>
<span class="channel-pct">100 %</span>
</div>
<div class="channel">
<span class="channel-label">Cb</span>
<div class="channel-bar"><div class="fill cb" style="width:25%"></div></div>
<span class="channel-pct">25 %</span>
</div>
<div class="channel">
<span class="channel-label">Cr</span>
<div class="channel-bar"><div class="fill cr" style="width:25%"></div></div>
<span class="channel-pct">25 %</span>
</div>
</div>
<div class="summary-line">
<span>Datenmenge:</span>
<span><strong>50 %</strong></span>
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Halbe Datenmenge. Identisches Bild — für dein Auge.</div>
<div class="verdict-a">JPEG, H.264, H.265, WebP — alle reduzieren die Farb-Auflösung auf <strong>ein Viertel</strong>. Das ist Psychovisualität: was wir nicht scharf sehen, muss man nicht scharf speichern.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1,212 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Raster vs. Vektor — Pixel oder Anweisung?</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--raster: #ea580c;
--vektor: #2563eb;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 24px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.pair {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
max-width: 1180px;
margin: 0 auto;
}
.col {
border: 2px solid;
border-radius: 12px;
padding: 16px 18px;
background: #fff;
display: flex;
flex-direction: column;
gap: 10px;
}
.col.raster { border-color: var(--raster); }
.col.vektor { border-color: var(--vektor); }
.col-head { display: flex; justify-content: space-between; align-items: baseline; }
.col-name { font-size: 1.3rem; font-weight: 700; }
.col.raster .col-name { color: var(--raster); }
.col.vektor .col-name { color: var(--vektor); }
.col-tag {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.col-desc { font-size: 0.85rem; color: var(--muted); line-height: 1.4; }
.views {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.view { display: flex; flex-direction: column; gap: 4px; }
.view-label {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
text-align: center;
}
.view-canvas {
background: var(--soft);
border: 1px solid var(--border);
border-radius: 6px;
aspect-ratio: 1.4;
overflow: hidden;
}
.view-canvas svg { display: block; width: 100%; height: 100%; }
.stats {
background: var(--soft);
border-radius: 6px;
padding: 8px 12px;
font-family: ui-monospace, monospace;
font-size: 0.82rem;
color: var(--dark);
line-height: 1.5;
}
.stats .key { color: var(--muted); }
.stats .val { font-weight: 700; }
.verdict {
margin-top: 22px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Raster vs. Vektor — Pixel oder Anweisung?</h1>
<div class="sub">Beide zeigen ein Häkchen. Wenn man <strong>reinzoomt</strong>, kommt die Wahrheit raus.</div>
<div class="pair">
<div class="col raster">
<div class="col-head">
<span class="col-name">Raster</span>
<span class="col-tag">PNG · JPEG · GIF</span>
</div>
<div class="col-desc">Ein Gitter aus Pixel-Farbwerten. Jede Position ist festgelegt.</div>
<div class="views">
<div class="view">
<div class="view-label">Original (32 × 22 px)</div>
<div class="view-canvas">
<svg viewBox="0 0 320 220" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="320" height="220" fill="#f8fafc"/>
<g shape-rendering="crispEdges">
<rect x="20" y="120" width="20" height="20" fill="#ea580c"/>
<rect x="40" y="140" width="20" height="20" fill="#ea580c"/>
<rect x="60" y="160" width="20" height="20" fill="#ea580c"/>
<rect x="80" y="180" width="20" height="20" fill="#ea580c"/>
<rect x="100" y="160" width="20" height="20" fill="#ea580c"/>
<rect x="120" y="140" width="20" height="20" fill="#ea580c"/>
<rect x="140" y="120" width="20" height="20" fill="#ea580c"/>
<rect x="160" y="100" width="20" height="20" fill="#ea580c"/>
<rect x="180" y="80" width="20" height="20" fill="#ea580c"/>
<rect x="200" y="60" width="20" height="20" fill="#ea580c"/>
<rect x="220" y="40" width="20" height="20" fill="#ea580c"/>
<rect x="240" y="20" width="20" height="20" fill="#ea580c"/>
</g>
</svg>
</div>
</div>
<div class="view">
<div class="view-label">4× Zoom</div>
<div class="view-canvas">
<svg viewBox="80 60 160 160" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="320" height="220" fill="#f8fafc"/>
<g shape-rendering="crispEdges">
<rect x="20" y="120" width="20" height="20" fill="#ea580c"/>
<rect x="40" y="140" width="20" height="20" fill="#ea580c"/>
<rect x="60" y="160" width="20" height="20" fill="#ea580c"/>
<rect x="80" y="180" width="20" height="20" fill="#ea580c"/>
<rect x="100" y="160" width="20" height="20" fill="#ea580c"/>
<rect x="120" y="140" width="20" height="20" fill="#ea580c"/>
<rect x="140" y="120" width="20" height="20" fill="#ea580c"/>
<rect x="160" y="100" width="20" height="20" fill="#ea580c"/>
<rect x="180" y="80" width="20" height="20" fill="#ea580c"/>
<rect x="200" y="60" width="20" height="20" fill="#ea580c"/>
<rect x="220" y="40" width="20" height="20" fill="#ea580c"/>
<rect x="240" y="20" width="20" height="20" fill="#ea580c"/>
</g>
</svg>
</div>
</div>
</div>
<div class="stats">
<div><span class="key">Speicher:</span> <span class="val">32 × 22 × 3 Byte = 2 112 Byte</span></div>
<div><span class="key">Beim Zoom:</span> <span class="val">pixelig — Information ist erschöpft</span></div>
</div>
</div>
<div class="col vektor">
<div class="col-head">
<span class="col-name">Vektor</span>
<span class="col-tag">SVG · PDF · Schriftarten</span>
</div>
<div class="col-desc">Mathematische Anweisungen: <code style="background:#0f0f23;color:#5fb3e4;padding:0 4px;border-radius:3px;">polyline 30,110 → 90,170 → 250,30</code></div>
<div class="views">
<div class="view">
<div class="view-label">Original</div>
<div class="view-canvas">
<svg viewBox="0 0 320 220" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="320" height="220" fill="#f8fafc"/>
<polyline points="30,130 90,190 270,30" stroke="#2563eb" stroke-width="14" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
<div class="view">
<div class="view-label">4× Zoom</div>
<div class="view-canvas">
<svg viewBox="80 60 160 160" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="320" height="220" fill="#f8fafc"/>
<polyline points="30,130 90,190 270,30" stroke="#2563eb" stroke-width="14" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
</div>
<div class="stats">
<div><span class="key">Speicher:</span> <span class="val">3 Punkte + Strichbreite = ~80 Byte</span></div>
<div><span class="key">Beim Zoom:</span> <span class="val">scharf — wird neu berechnet</span></div>
</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Pixel sind festgenagelt. Vektoren werden bei jedem Zoom <strong>neu berechnet</strong>.</div>
<div class="verdict-a">Faustregel: <strong>Foto → Raster</strong> (kann nicht „neu berechnet" werden). <strong>Logo, Icon, Schrift → Vektor</strong> (skalierbar ohne Verlust).</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1,189 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Was wir über Kompression gelernt haben</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--lossless: #16a34a;
--lossy: #ef4444;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 26px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.compare {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 22px;
max-width: 1180px;
margin: 0 auto;
}
.col {
border: 2px solid;
border-radius: 12px;
padding: 18px 22px;
background: #fff;
display: flex;
flex-direction: column;
gap: 12px;
}
.col.lossless { border-color: var(--lossless); }
.col.lossy { border-color: var(--lossy); }
.col-head { display: flex; justify-content: space-between; align-items: baseline; }
.col-name { font-size: 1.4rem; font-weight: 700; }
.col.lossless .col-name { color: var(--lossless); }
.col.lossy .col-name { color: var(--lossy); }
.col-tag {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.row { display: grid; grid-template-columns: 120px 1fr; gap: 10px 14px; }
.row-key {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--muted);
padding-top: 3px;
}
.row-val { font-size: 0.92rem; line-height: 1.5; color: var(--dark); }
.row-val strong {
font-family: ui-monospace, monospace;
background: var(--soft);
padding: 1px 6px;
border-radius: 4px;
}
.matrix {
margin-top: 22px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
background: var(--soft);
border: 1.5px solid var(--border);
border-radius: 10px;
padding: 14px 22px;
}
.matrix-head {
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
margin-bottom: 8px;
}
.matrix-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 8px 16px;
font-size: 0.88rem;
}
.matrix-grid .scenario { font-weight: 700; color: var(--dark); }
.matrix-grid .answer {
font-family: ui-monospace, monospace;
color: var(--dark);
}
.matrix-grid .answer.lossless { color: var(--lossless); font-weight: 700; }
.matrix-grid .answer.lossy { color: var(--lossy); font-weight: 700; }
.verdict {
margin-top: 18px;
max-width: 1180px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.93rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Was wir über Kompression gelernt haben</h1>
<div class="sub">Zwei fundamental verschiedene Wege, dasselbe Ziel: <strong>Daten kleiner machen.</strong></div>
<div class="compare">
<div class="col lossless">
<div class="col-head">
<span class="col-name">Verlustfrei</span>
<span class="col-tag">Lossless</span>
</div>
<div class="row">
<div class="row-key">Was passiert</div>
<div class="row-val">Redundanz raus — Wiederholungen kürzer schreiben</div>
<div class="row-key">Umkehrbar</div>
<div class="row-val">Ja. Aus der komprimierten Datei wird das <strong>exakte</strong> Original wieder.</div>
<div class="row-key">Verfahren</div>
<div class="row-val">RLE, Huffman, Deflate, LZ77/LZ78</div>
<div class="row-key">Formate</div>
<div class="row-val">ZIP · PNG · FLAC · TIFF · RAW</div>
<div class="row-key">Faktor</div>
<div class="row-val">2× – 5× (typisch)</div>
</div>
</div>
<div class="col lossy">
<div class="col-head">
<span class="col-name">Verlustbehaftet</span>
<span class="col-tag">Lossy</span>
</div>
<div class="row">
<div class="row-key">Was passiert</div>
<div class="row-val">Irrelevanz raus — was Mensch nicht wahrnimmt, wird weggeworfen</div>
<div class="row-key">Umkehrbar</div>
<div class="row-val">Nein. Einmal weg, für immer weg.</div>
<div class="row-key">Trick</div>
<div class="row-val">Psychoakustik (MP3) · Psychovisualität (JPEG)</div>
<div class="row-key">Formate</div>
<div class="row-val">MP3 · AAC · JPEG · WebP · H.264 · H.265</div>
<div class="row-key">Faktor</div>
<div class="row-val">10× – 100× (typisch)</div>
</div>
</div>
</div>
<div class="matrix">
<div class="matrix-head">Entscheidungsmatrix — wann nehme ich welches?</div>
<div class="matrix-grid">
<div class="scenario">Foto-Archiv (RAW)</div>
<div class="answer lossless">Lossless</div>
<div class="scenario">Spotify-Stream</div>
<div class="answer lossy">Lossy</div>
<div class="scenario">Code-Repository</div>
<div class="answer lossless">Lossless (ZIP)</div>
<div class="scenario">Instagram-Foto</div>
<div class="answer lossy">Lossy (JPEG)</div>
<div class="scenario">Studio-Master-Track</div>
<div class="answer lossless">Lossless (FLAC)</div>
<div class="scenario">YouTube-Video</div>
<div class="answer lossy">Lossy (H.264)</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Lossless für Archiv und Code. Lossy für Streaming und Anzeige.</div>
<div class="verdict-a">Faustregel: <strong>Werkzeug</strong> (du arbeitest noch dran) → Lossless. <strong>Konsum</strong> (du schaust/hörst nur) → Lossy.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

@@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Was wir in 90 Minuten gelernt haben</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--left: #d63384;
--right: #0891b2;
--bytes: #0891b2;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 30px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 28px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.recap {
display: grid;
grid-template-columns: 160px 1fr 200px 1fr 160px;
gap: 10px;
max-width: 1200px;
margin: 0 auto;
align-items: center;
}
.station {
border: 2px solid var(--border);
border-radius: 10px;
padding: 18px 12px;
background: #fff;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.station.bytes { border-color: var(--bytes); background: #0f0f23; color: #fff; }
.station-tag {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--muted);
}
.station.bytes .station-tag { color: #94a3b8; }
.station-name { font-size: 1.3rem; font-weight: 700; }
.station.bytes .station-name { color: #67e8f9; }
.station-detail { font-size: 0.78rem; color: var(--muted); line-height: 1.35; }
.station.bytes .station-detail { color: #94a3b8; font-family: ui-monospace, monospace; font-size: 0.76rem; line-height: 1.35; }
.strang {
display: flex;
flex-direction: column;
gap: 6px;
padding: 0 6px;
}
.strang-arrow { text-align: center; }
.strang-arrow svg { width: 100%; height: 18px; }
.strang-title {
font-size: 0.92rem;
font-weight: 700;
text-align: center;
line-height: 1.3;
}
.strang.left .strang-title { color: var(--left); }
.strang.right .strang-title { color: var(--right); }
.strang-list {
display: flex;
flex-direction: column;
gap: 3px;
list-style: none;
margin: 0;
padding: 0;
background: var(--soft);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 12px;
}
.strang-list li {
font-size: 0.82rem;
color: var(--dark);
padding-left: 12px;
position: relative;
line-height: 1.4;
}
.strang-list li::before {
content: "·";
position: absolute;
left: 0;
top: 0;
font-weight: 700;
}
.strang.left .strang-list li::before { color: var(--left); }
.strang.right .strang-list li::before { color: var(--right); }
.verdict {
margin-top: 22px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.1rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.95rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>Was wir in 90 Minuten gelernt haben</h1>
<div class="sub">Beide Wege gehen durch <strong>dieselbe Mitte</strong>. Du kennst jetzt alle Stationen.</div>
<div class="recap">
<div class="station">
<div class="station-tag">Quelle</div>
<div class="station-name">Welt</div>
<div class="station-detail">Schallwelle<br>Lichtwelle<br>kontinuierlich · analog</div>
</div>
<div class="strang left">
<div class="strang-arrow">
<svg viewBox="0 0 200 18" preserveAspectRatio="none">
<line x1="6" y1="9" x2="180" y2="9" stroke="#d63384" stroke-width="2"/>
<path d="M174,3 L188,9 L174,15" stroke="#d63384" stroke-width="2" fill="none"/>
</svg>
</div>
<div class="strang-title">Welt → Byte</div>
<ul class="strang-list">
<li>Sampling-Rate</li>
<li>Bittiefe · Quantisierung</li>
<li>Datenrate-Formel</li>
<li>Nyquist · 2·f<sub>max</sub></li>
<li>Aliasing &amp; Moiré</li>
<li>44,1 kHz = 2·20 kHz + Marge</li>
</ul>
</div>
<div class="station bytes">
<div class="station-tag">Mitte</div>
<div class="station-name">Datei</div>
<div class="station-detail">
Byte-Strom<br>
89 50 4E 47<br>
nichts als Zahlen
</div>
</div>
<div class="strang right">
<div class="strang-arrow">
<svg viewBox="0 0 200 18" preserveAspectRatio="none">
<line x1="6" y1="9" x2="180" y2="9" stroke="#0891b2" stroke-width="2"/>
<path d="M174,3 L188,9 L174,15" stroke="#0891b2" stroke-width="2" fill="none"/>
</svg>
</div>
<div class="strang-title">Byte → Bedeutung</div>
<ul class="strang-list">
<li>Bit · Byte · Hex</li>
<li>8 Bit = 256 Zustände</li>
<li>ASCII · UTF-8</li>
<li>Magic Number</li>
<li>KB vs KiB</li>
<li>Hex im Alltag (CSS, MAC)</li>
</ul>
</div>
<div class="station">
<div class="station-tag">Ziel</div>
<div class="station-name">Mensch</div>
<div class="station-detail">Text · Bild · Klang<br>Bedeutung<br>verstehen</div>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Eine Datei ist kein Mysterium. Es ist eine <strong>Reihe von Zahlen</strong> mit zwei Geschichten dran.</div>
<div class="verdict-a">Eine Geschichte erzählt, <strong>woher</strong> die Zahlen kommen. Die andere, <strong>was</strong> sie bedeuten. Beide kennst du jetzt.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1,217 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>USB-C — drei Achsen, eine Verwirrung</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--soft: #f8fafc;
--border: #cbd5e1;
--hl: #d63384;
--stecker: #2563eb;
--kabel: #ea580c;
--protokoll: #7c3aed;
}
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 40px; }
h1 { text-align: center; margin: 0 0 6px; font-size: 1.75rem; }
.sub { text-align: center; color: var(--muted); margin-bottom: 22px; font-size: 1.05rem; }
.sub strong { color: var(--dark); }
.axes {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 18px;
max-width: 1200px;
margin: 0 auto;
}
.axis {
border: 2px solid;
border-radius: 12px;
padding: 14px 16px;
background: #fff;
display: flex;
flex-direction: column;
gap: 10px;
}
.axis.stecker { border-color: var(--stecker); }
.axis.kabel { border-color: var(--kabel); }
.axis.protokoll { border-color: var(--protokoll); }
.axis-head { display: flex; justify-content: space-between; align-items: baseline; }
.axis-num {
font-family: ui-monospace, monospace;
font-size: 0.72rem;
font-weight: 700;
color: var(--muted);
}
.axis-name { font-size: 1.15rem; font-weight: 700; }
.axis.stecker .axis-name { color: var(--stecker); }
.axis.kabel .axis-name { color: var(--kabel); }
.axis.protokoll .axis-name { color: var(--protokoll); }
.axis-tagline {
font-size: 0.82rem;
color: var(--muted);
line-height: 1.4;
}
.axis-tagline strong { color: var(--dark); }
.icon {
background: var(--soft);
border-radius: 6px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.icon svg { display: block; }
.options {
font-size: 0.82rem;
line-height: 1.6;
color: var(--dark);
background: var(--soft);
border-radius: 6px;
padding: 10px 14px;
}
.options strong {
font-family: ui-monospace, monospace;
font-size: 0.78rem;
padding: 1px 5px;
border-radius: 3px;
background: #fff;
border: 1px solid var(--border);
}
.axis.stecker .options strong { color: var(--stecker); }
.axis.kabel .options strong { color: var(--kabel); }
.axis.protokoll .options strong { color: var(--protokoll); }
.scenario {
margin-top: 22px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
background: var(--soft);
border: 1.5px solid var(--border);
border-radius: 10px;
padding: 14px 22px;
}
.scenario-head {
font-size: 0.74rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--muted);
margin-bottom: 6px;
}
.scenario-body {
font-size: 0.92rem;
line-height: 1.5;
}
.scenario-body code {
font-family: ui-monospace, monospace;
background: #fff;
padding: 1px 6px;
border-radius: 4px;
border: 1px solid var(--border);
}
.verdict {
margin-top: 16px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
text-align: center;
border: 2px solid var(--hl);
border-radius: 12px;
padding: 14px 22px;
}
.verdict-q { font-size: 1.05rem; font-weight: 700; color: var(--hl); }
.verdict-a { font-size: 0.92rem; color: var(--dark); margin-top: 4px; }
.verdict-a strong { color: var(--hl); }
</style>
</head>
<body>
<h1>USB-C — drei Achsen, eine Verwirrung</h1>
<div class="sub">Derselbe Stecker. <strong>Aber Kabel ≠ Kabel.</strong> <strong>Protokoll ≠ Protokoll.</strong></div>
<div class="axes">
<div class="axis stecker">
<div class="axis-head">
<span class="axis-name">Stecker</span>
<span class="axis-num">1 · MECHANIK</span>
</div>
<div class="axis-tagline">Die <strong>Form</strong>. Was reinsteckt, oval, beidseitig.</div>
<div class="icon">
<svg viewBox="0 0 120 30" width="140" height="40">
<rect x="6" y="9" width="108" height="12" rx="6" fill="#2563eb" opacity="0.18" stroke="#2563eb" stroke-width="2"/>
<rect x="22" y="13" width="76" height="4" rx="2" fill="#2563eb"/>
</svg>
</div>
<div class="options">
Nur eine Sorte: <strong>USB-C</strong> (auch USB Type-C). 24 Pins. Beidseitig steckbar. Andere Stecker-Typen: USB-A, USB-B, Micro-USB — nicht mit C kompatibel.
</div>
</div>
<div class="axis kabel">
<div class="axis-head">
<span class="axis-name">Kabel</span>
<span class="axis-num">2 · WAS DURCH-PASST</span>
</div>
<div class="axis-tagline">Die <strong>Adern</strong>. Was elektrisch transportiert werden kann.</div>
<div class="icon">
<svg viewBox="0 0 160 30" width="180" height="40">
<path d="M10,15 L150,15" stroke="#ea580c" stroke-width="3"/>
<rect x="2" y="8" width="14" height="14" rx="3" fill="#ea580c" opacity="0.4" stroke="#ea580c"/>
<rect x="144" y="8" width="14" height="14" rx="3" fill="#ea580c" opacity="0.4" stroke="#ea580c"/>
</svg>
</div>
<div class="options">
<strong>Charge-only</strong> · billig, nur Strom · 5 W<br>
<strong>USB 2.0</strong> · 480 Mbit/s · 60 W (3 A)<br>
<strong>USB 3.2 Gen 2</strong> · 10 Gbit/s · 100 W<br>
<strong>Thunderbolt 4</strong> · 40 Gbit/s · 240 W
</div>
</div>
<div class="axis protokoll">
<div class="axis-head">
<span class="axis-name">Protokoll</span>
<span class="axis-num">3 · WAS LÄUFT</span>
</div>
<div class="axis-tagline">Die <strong>Sprache</strong>. Was die zwei Geräte miteinander reden.</div>
<div class="icon">
<svg viewBox="0 0 160 30" width="180" height="40">
<circle cx="20" cy="15" r="6" fill="#7c3aed"/>
<path d="M30,15 L130,15" stroke="#7c3aed" stroke-width="2" stroke-dasharray="6,4"/>
<circle cx="140" cy="15" r="6" fill="#7c3aed"/>
</svg>
</div>
<div class="options">
<strong>USB-PD</strong> · Power Delivery (Laden)<br>
<strong>USB 2/3/4</strong> · Datenübertragung<br>
<strong>Thunderbolt 3/4</strong> · Daten + Display<br>
<strong>DisplayPort Alt</strong> · Monitor anschließen<br>
<strong>HDMI Alt</strong> · weiter HDMI rausführen
</div>
</div>
</div>
<div class="scenario">
<div class="scenario-head">Konkret: warum lädt der Laptop nicht?</div>
<div class="scenario-body">
USB-C-Port am Laptop ist da (<strong>Stecker</strong> stimmt). Aber der Aldi-Kabel ist <code>Charge-only</code> mit <code>5 W</code> (<strong>Kabel</strong> kann nicht genug Strom liefern). Oder der Port am Laptop unterstützt nur <code>USB-PD</code> mit 100 W, das Netzteil meldet aber nur 65 W (<strong>Protokoll</strong>-Handshake scheitert). <em>Eine der drei Achsen passt nicht.</em>
</div>
</div>
<div class="verdict">
<div class="verdict-q">Drei Achsen. Drei unabhängige Fragen. Drei Gründe, warum's nicht funktioniert.</div>
<div class="verdict-a">Beim nächsten „USB-C-Kabel funktioniert nicht": <strong>Stecker passt → Kabel-Spec checken → Protokoll-Support prüfen.</strong> In dieser Reihenfolge.</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

File diff suppressed because it is too large. Load diff
+7 -7
View File
@@ -97,7 +97,7 @@ Hochschule der Medien Stuttgart
<!-- _class: lead --> <!-- _class: lead -->
# Herzlich willkommen! # Herzlich willkommen
## Einführung ## Einführung
--- ---
@@ -116,13 +116,13 @@ Hochschule der Medien Stuttgart
--- ---
# Warum dieses Modul? # Motivation für dieses Modul
**Das Internet ist überall** – aber was steckt dahinter? **Das Internet ist überall** – aber was steckt dahinter?
* Warum sehen Webseiten auf dem Handy anders aus? * Warum sehen Webseiten auf dem Handy anders aus?
* Wie funktioniert eigentlich das Internet? * Wie funktioniert eigentlich das Internet?
* Wie erkennen Sie, ob eine Werbeagentur gute Arbeit leistet? * Wie erkennt ihr, ob eine Werbeagentur gute Arbeit leistet?
* Was hat ein Unterseekabel mit meiner E-Mail zu tun? * Was hat ein Unterseekabel mit meiner E-Mail zu tun?
* **→ Die Technik dahinter verstehen und fundierte Entscheidungen treffen** * **→ Die Technik dahinter verstehen und fundierte Entscheidungen treffen**
@@ -136,13 +136,13 @@ Als Digital- und MedienwirtschaftlerInnen werdet ihr täglich mit technischen Fr
* Wie baue ich eine barrierearme Webseite? * Wie baue ich eine barrierearme Webseite?
* Welches Protokoll für welchen Zweck? * Welches Protokoll für welchen Zweck?
* Wie funktioniert das Web hinter den Kulissen? * Wie funktioniert das Web hinter den Kulissen?
* **→ Mitreden können – und selbst Webseiten bauen!** * **→ Mitreden können – und selbst Webseiten bauen**
--- ---
# Was Sie hier lernen werden # Lernziele
**In dieser Veranstaltung erwerben Sie folgende Kompetenzen:** **In dieser Veranstaltung erwerbt ihr folgende Kompetenzen:**
* Geschichte des Computers und Internets * Geschichte des Computers und Internets
* HTML-Grundlagen und semantisches Markup * HTML-Grundlagen und semantisches Markup
@@ -172,7 +172,7 @@ Als Digital- und MedienwirtschaftlerInnen werdet ihr täglich mit technischen Fr
2. Netzwerke, Protokolle, **semantisches HTML** & **CSS** 2. Netzwerke, Protokolle, **semantisches HTML** & **CSS**
3. Interaktivität, Animationen & **JavaScript** 3. Interaktivität, Animationen & **JavaScript**
**Ziel:** "Gelernte Hilflosigkeit" ablegen **Ziel:** Technische Scheu abbauen und Web-Grundlagen selbst beherrschen
--- ---
+114 -52
View File
@@ -72,18 +72,6 @@ section.aufgabe footer {
} }
section.erklaerung { section.erklaerung {
font-size: 1.1rem; font-size: 1.1rem;
/**background: repeating-linear-gradient(
135deg,
#fce4ec,
#fce4ec 40px,
#fff 40px,
#fff 80px
) !important;**/
}
@media print {
section.erklaerung {
background: #fce4ec !important;
}
} }
section.erklaerung h1 { section.erklaerung h1 {
font-size: 1.5rem; font-size: 1.5rem;
@@ -138,7 +126,7 @@ Hochschule der Medien Stuttgart
2. Netzwerke, Protokolle, **semantisches HTML** & **CSS** 2. Netzwerke, Protokolle, **semantisches HTML** & **CSS**
3. Interaktivität, Animationen & **JavaScript** 3. Interaktivität, Animationen & **JavaScript**
**Ziel:** "Gelernte Hilflosigkeit" ablegen **Ziel:** Technische Scheu abbauen und Web-Grundlagen selbst beherrschen
--- ---
@@ -415,10 +403,10 @@ Der Turing-Test (1950): Ein Interrogator kommuniziert per Text mit einem Mensche
- Implosions-Simulationen - Implosions-Simulationen
- Nuklearphysik-Gleichungen - Nuklearphysik-Gleichungen
**Lösung 1943:** Menschliche "Computer" **Lösung 1943:** Die Berechnungen übernehmen menschliche "Computer"
→ Mathematikerinnen mit Taschenrechnern & IBM-Lochkarten → Mathematikerinnen mit Taschenrechnern & IBM-Lochkarten
**Zu langsam** → Bedarf an automatischer Berechnung **Das ist zu langsam** → es entsteht der Bedarf an automatischer Berechnung
**Schlüsselfigur:** John von Neumann (Mathematik & Stoßwellen) **Schlüsselfigur:** John von Neumann (Mathematik & Stoßwellen)
@@ -439,7 +427,7 @@ Das ENIAC-Projekt in Philadelphia sollte die Berechnungen beschleunigen.
# John von Neumann (1903–1957) # John von Neumann (1903–1957)
- Geboren in Budapest, Österreich-Ungarn - Geboren in Budapest, Österreich-Ungarn
- Mit 8: Konnte Telefonbücher auswendig - Mit 8: Konnte ganze Telefonbücher auswendig aufsagen
- Mit 22: Doktor in Mathematik - Mit 22: Doktor in Mathematik
**Beiträge:** **Beiträge:**
@@ -635,14 +623,14 @@ COBOL läuft heute noch auf Bankensystemen
- Jeder Befehl entspricht direkt einer CPU-Instruktion - Jeder Befehl entspricht direkt einer CPU-Instruktion
- Hardwarenah, aber für Menschen lesbar - Hardwarenah, aber für Menschen lesbar
**Fun Fact:** Chris Sawyer schrieb **RollerCoaster Tycoon** (1999) fast komplett in x86-Assembly **Kuriosum:** Chris Sawyer schrieb **RollerCoaster Tycoon** (1999) fast komplett in x86-Assembly
<!-- <!--
KATHLEEN BOOTH (1922–2022): KATHLEEN BOOTH (1922–2022):
Kathleen Hylda Valerie Booth (geb. Britten), BSc Mathematik (Royal Holloway, London), PhD Applied Mathematics (King's College London). Arbeitete ab 1946 mit Andrew Booth (später ihr Ehemann) am Birkbeck College – eines der kleinsten Computing-Teams Großbritanniens: nur Andrew, Kathleen und Xenia Sweeting. Kathleen Hylda Valerie Booth (geb. Britten), BSc Mathematik (Royal Holloway, London), PhD Applied Mathematics (King's College London). Arbeitete ab 1946 mit Andrew Booth (später ihr Ehemann) am Birkbeck College – eines der kleinsten Computing-Teams Großbritanniens: nur Andrew, Kathleen und Xenia Sweeting.
WAS SIE TAT: WAS SIE TAT:
Kathleen baute die Hardware des ARC (Automatic Relay Calculator) mit auf und entwickelte dann die Programmierarchitektur. 1947 reiste sie mit Andrew nach Princeton, wo sie John von Neumann trafen und die Stored-Program-Architektur kennenlernten. Sie schrieb "Coding for A.R.C." – die erste veröffentlichte Beschreibung einer Assembly-Sprache. Statt roher Binärzahlen (z.B. 10011) konnten Programmierer nun mnemonische Kürzel verwenden (z.B. M -> cR). Ein konzeptioneller Sprung – die erste Abstraktionsschicht zwischen Mensch und Maschine. Kathleen baute die Hardware des ARC (Automatic Relay Calculator) mit auf und entwickelte dann die Programmierarchitektur. 1947 reiste sie mit Andrew nach Princeton, wo sie John von Neumann trafen und die Stored-Program-Architektur kennenlernten. Sie schrieb "Coding for A.R.C." – die erste veröffentlichte Beschreibung einer Assembly-Sprache. Statt roher Binärzahlen (z.B. 10011) konnten Programmierende nun mnemonische Kürzel verwenden (z.B. M -> cR). Ein konzeptioneller Sprung – die erste Abstraktionsschicht zwischen Mensch und Maschine.
VOR ASSEMBLER: VOR ASSEMBLER:
ENIAC (1943–46) wurde durch physisches Umstecken von Kabeln programmiert – Programmwechsel dauerten Tage. Danach kamen Stored-Program-Computer, aber Programme mussten als rohe Binärzahlen eingegeben werden. Jeder Tippfehler in einer 0/1-Folge konnte den Befehl komplett verfälschen. ENIAC (1943–46) wurde durch physisches Umstecken von Kabeln programmiert – Programmwechsel dauerten Tage. Danach kamen Stored-Program-Computer, aber Programme mussten als rohe Binärzahlen eingegeben werden. Jeder Tippfehler in einer 0/1-Folge konnte den Befehl komplett verfälschen.
@@ -726,7 +714,7 @@ Cambridge Analytica (2018): Facebook-Daten von 87 Mio. Nutzern wurden für polit
China Social Credit System: Punktesystem für "gutes Verhalten". Wer zu oft bei Rot geht, bekommt keinen Kredit mehr. China Social Credit System: Punktesystem für "gutes Verhalten". Wer zu oft bei Rot geht, bekommt keinen Kredit mehr.
Die Frage: Wer entscheidet, was mit Technologie gemacht wird? Die Entwickler? Die Firmen? Die Politik? Wir alle? Die Frage: Wer entscheidet, was mit Technologie gemacht wird? Die Entwickelnden? Die Firmen? Die Politik? Wir alle?
--> -->
--- ---
@@ -905,7 +893,7 @@ Geprägt 1948 von Claude Shannon
<!-- <!--
Alles Digitale basiert darauf Alles Digitale basiert darauf
Transistoren in CPUs: Milliarden Bits schalten Millionen Mal pro Sekunde Transistoren in CPUs: Milliarden Bit schalten Millionen Mal pro Sekunde
Shannon: Begründer der Informationstheorie Shannon: Begründer der Informationstheorie
--> -->
@@ -913,31 +901,58 @@ Shannon: Begründer der Informationstheorie
# Das Byte # Das Byte
**8 Bit = 1 Byte** ### Die kleinste *adressierbare* Informationseinheit
### 8 bit = 1 Byte
``` ```
0 0 1 0 1 0 1 0 0 0 1 0 1 0 1 0 = 42
``` ```
**Wie viele Kombinationen?**
2⁸ = 2 x 2 x 2 x 2 x 2 x 2 x 2 x 2 = **256 Möglichkeiten** (0-255)
<!-- <!--
BYTE = Wortspiel aus "Bit" + "Bite" (Bissen) BYTE = Wortspiel aus "Bit" + "Bite" (Bissen) — Begriff geprägt von Werner Buchholz (IBM, 1956)
Warum 8 Bits? IBM System/360 (1964) setzte Standard Schreibweise mit "y" statt "i": bewusste Änderung zur Vermeidung von Verwechslung mit "Bit"
2×2×2×2×2×2×2×2 = 256 binary 00101010 = decimal 42
--> -->
--- ---
# Was kann man mit 256 Zuständen machen? # Das Byte
### 8 bit = 1 Byte
```
0 0 0 0 0 0 0 0 = 0
0 0 0 0 0 0 0 1 = 1
0 0 0 0 0 0 1 0 = 2
0 0 0 0 0 0 1 1 = 3
0 0 0 0 0 1 0 0 = 4
0 1 0 0 0 0 0 0 = 64
0 1 1 1 1 1 1 1 = 127
1 1 1 1 1 1 1 1 = ?
```
2 × 2 × 2 × 2 × 2 × 2 × 2 × 2 = 2⁸ = **256 mögliche Zustände**
<!--
- 1964: IBM System/360 setzte den 8-Bit-Byte-Standard — vorher 6-Bit/7-Bit-Systeme
- ASCII (1963) brauchte 7 Bit für 128 Zeichen
- 8 Bit = praktisch für Hardware (Zweierpotenz: 2³ = 8)
- 8 Bit = 2 Hexadezimalziffern (elegante Darstellung)
- Kleinste adressierbare Einheit im Speicher: Prozessor kann nicht einzelne Bit direkt ansprechen, immer Byte
-->
---
# 256 Zustände – was sich damit darstellen lässt
- **256 Zeichen** (Buchstaben, Zahlen, Symbole) - **256 Zeichen** (Buchstaben, Zahlen, Symbole)
- **256 Graustufen** (0 = Schwarz, 255 = Weiß) - **256 Graustufen** (0 = Schwarz, 255 = Weiß)
- **256 Lautstärkestufen** - **256 Lautstärkestufen**
- **Zahlen 0-255** - **Zahlen 0-255**
**Für Farbe:** 3 Bytes pro Pixel (RGB) **Für Farbe:** 3 Byte pro Pixel (RGB)
R=0-255, G=0-255, B=0-255 → **16,7 Millionen Farben** R=0-255, G=0-255, B=0-255 → **16,7 Millionen Farben**
<!-- <!--
@@ -957,6 +972,22 @@ Farbbild: Rot + Grün + Blau
Kernidee: jedes Byte lässt sich sauber in zwei 4-Bit-Hälften (Nibbles) zerlegen. Jede Hälfte hat 2⁴ = 16 Zustände – und genau 16 Symbole hat Hex (0-F). Deshalb passt Hex perfekt: 1 Nibble = 1 Hex-Ziffer, 1 Byte = 2 Hex-Ziffern. Keine krumme Umrechnung. Kernidee: jedes Byte lässt sich sauber in zwei 4-Bit-Hälften (Nibbles) zerlegen. Jede Hälfte hat 2⁴ = 16 Zustände – und genau 16 Symbole hat Hex (0-F). Deshalb passt Hex perfekt: 1 Nibble = 1 Hex-Ziffer, 1 Byte = 2 Hex-Ziffern. Keine krumme Umrechnung.
--> -->
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/why-8-bit.png)
<!--
Warum gerade 8 Bit?
- CPU adressiert byteweise — kleinste adressierbare Einheit
- Halbe Byte (z.B. 0x0000.5) existieren nicht
- Speichercontroller, Bus, CPU-Register sind alle auf 8-Bit-Häppchen ausgelegt
- Einzelne Bit lesen: erst Byte holen, dann mit Bitmaske isolieren (byte & 0b1000_0000)
- Hardware-Geschichte: IBM System/360 (1964) setzte 8-Bit-Standard, 7-Bit-ASCII + 1 Paritätsbit
-->
<!-- <!--
# Zahlensysteme # Zahlensysteme
@@ -973,7 +1004,7 @@ Kernidee: jedes Byte lässt sich sauber in zwei 4-Bit-Hälften (Nibbles) zerlege
<!-- <!--
Dezimal = was wir im Alltag nutzen (10 Finger → 10 Ziffern) Dezimal = was wir im Alltag nutzen (10 Finger → 10 Ziffern)
Binär = was Computer nutzen (Strom an/aus → 2 Zustände) Binär = was Computer nutzen (Strom an/aus → 2 Zustände)
Hexadezimal = Kompromiss: kompakter als Binär, aber passt perfekt auf Bytes (2 Hex-Ziffern = 1 Byte = 8 Bit) Hexadezimal = Kompromiss: kompakter als Binär, aber passt perfekt auf Byte (2 Hex-Ziffern = 1 Byte = 8 Bit)
Die Basis bestimmt, wie viele Symbole pro Stelle zur Verfügung stehen: Die Basis bestimmt, wie viele Symbole pro Stelle zur Verfügung stehen:
- Dezimal: 10 (0–9) - Dezimal: 10 (0–9)
- Binär: 2 (0, 1) - Binär: 2 (0, 1)
@@ -990,7 +1021,7 @@ Die Basis bestimmt, wie viele Symbole pro Stelle zur Verfügung stehen:
**Hexadezimal (Base 16):** **Hexadezimal (Base 16):**
`4D 50 33` (= "MP3" in ASCII) `4D 50 33` (= "MP3" in ASCII)
**Jede Hex-Ziffer = 4 Bits (ein "Nibble")** **Jede Hex-Ziffer = 4 Bit (ein "Nibble")**
0-9, A-F (A=10, B=11, ..., F=15) 0-9, A-F (A=10, B=11, ..., F=15)
`4 = 0100` `D = 1101` `4 = 0100` `D = 1101`
@@ -1000,7 +1031,7 @@ Die Basis bestimmt, wie viele Symbole pro Stelle zur Verfügung stehen:
<!-- <!--
Hex = Shortcut für Menschen Hex = Shortcut für Menschen
1 Byte = 2 Hex-Ziffern (00-FF) 1 Byte = 2 Hex-Ziffern (00-FF)
"Nibble" = 4 Bits = halbes Byte (Wortspiel: nibble = knabbern, byte = beißen) "Nibble" = 4 Bit = halbes Byte (Wortspiel: nibble = knabbern, byte = beißen)
CSS (Cascading Style Sheets)-Farben nutzen Hex: #FF0000 = Rot CSS (Cascading Style Sheets)-Farben nutzen Hex: #FF0000 = Rot
--> -->
@@ -1013,7 +1044,39 @@ CSS (Cascading Style Sheets)-Farben nutzen Hex: #FF0000 = Rot
--- ---
# Wo begegnet Ihnen Hex-Code? <!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/byte-flow.png)
<!--
1 Byte = 8 Bit = 2 Hex-Ziffern = 1 ASCII-Zeichen
- Dieselbe Datei, drei Schreibweisen
- Jeder Rahmen = ein Byte
- Byte ändern sich nicht, nur unsere Anzeige
- ↵ (0x0A) = nicht druckbar → Hex-Editoren zeigen . als Platzhalter
-->
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/three-views.png)
<!--
Dieselben 8 Byte (PNG-Dateianfang: 89 50 4E 47 0D 0A 1A 0A) — drei Perspektiven:
1. Bitstream — was wirklich gespeichert wird (unleserlich)
2. Hex — gruppiert in 8-Bit-Häppchen (kompakt)
3. Bedeutung — was die Byte signalisieren:
- 89: Magic Byte (>127 → "ich bin Binärdatei")
- 50 4E 47: P N G (ASCII-Format-Kürzel)
- 0D 0A 1A 0A: CR LF EOF LF (erkennt kaputte Übertragung)
-->
---
# Wo euch Hex-Code begegnet
| Kontext | Beispiel | | Kontext | Beispiel |
|---------|----------| |---------|----------|
@@ -1049,7 +1112,7 @@ Fehlercodes: Windows zeigt diese bei Bluescreens
--- ---
# Bit vs. Byte: Die Verwirrung # Bit vs. Byte: der Unterschied
**1 Byte = 8 Bit** → Bit ÷ 8 = Byte **1 Byte = 8 Bit** → Bit ÷ 8 = Byte
@@ -1063,7 +1126,7 @@ Fehlercodes: Windows zeigt diese bei Bluescreens
<!-- <!--
Bits = kleines b, Bytes = großes B Bit = kleines b, Byte = großes B
Internetanbieter: Mbit/s (klingt größer!) Internetanbieter: Mbit/s (klingt größer!)
100 Mbit/s ÷ 8 = 12,5 MB/s 100 Mbit/s ÷ 8 = 12,5 MB/s
Warum? Marketing - 100 klingt besser als 12,5 Warum? Marketing - 100 klingt besser als 12,5
@@ -1083,7 +1146,7 @@ Warum? Marketing - 100 klingt besser als 12,5
**H**yper**T**ext **M**arkup **L**anguage **H**yper**T**ext **M**arkup **L**anguage
* **Keine** Programmiersprache * **Keine** Programmiersprache
* Am ehesten eine Formattierungssprache * Am ehesten eine Auszeichnungssprache (Markup)
* Beschreibt **Struktur und Aufbau** einer Webseite * Beschreibt **Struktur und Aufbau** einer Webseite
* ```html * ```html
<p>Das ist ein Absatz.</p> <p>Das ist ein Absatz.</p>
@@ -1104,8 +1167,8 @@ Aktuelle Version: HTML5 (seit 2014)
# Was ist HTML? (2/2) # Was ist HTML? (2/2)
* Mit HTML werden **HTML-Dokumente** beschrieben (bspw. `index.html`) * Mit HTML werden **HTML-Dokumente** beschrieben (bspw. `index.html`)
* Ein HTML-Dokument beschreibt das/den `Document Object Model (DOM)` * Ein HTML-Dokument beschreibt das **Document Object Model (DOM)**
* Der DOM ist eine **hierarchische Baumstruktur** (Kind- und Elternelemente) * Das DOM ist eine **hierarchische Baumstruktur** (Kind- und Elternelemente)
* *parent nodes / elements* * *parent nodes / elements*
* *child nodes / elements* * *child nodes / elements*
* *sibling nodes / elements* * *sibling nodes / elements*
@@ -1115,12 +1178,12 @@ Aktuelle Version: HTML5 (seit 2014)
# HTML-Anatomie (1/2) # HTML-Anatomie (1/2)
Formattierungssprache HTML ist eine Auszeichnungssprache (Markup).
* HTML folgt einer strengen **Syntax** zur Formattieren von digitalen Inhalten * HTML folgt einer strengen **Syntax** zum Auszeichnen digitaler Inhalte
* Syntax: `<element attribute="value"> ... </element>` * Syntax: `<element attribute="value"> ... </element>`
* Öffnende und schließende HTML-Tags müssen identisch sein * Öffnende und schließende HTML-Tags müssen identisch sein
* Das schließende Tag muss ein `/` nach der Spitzenklammer haben * Das schließende Tag hat ein `/` direkt nach der öffnenden spitzen Klammer (`</p>`)
--- ---
@@ -1136,7 +1199,7 @@ Formattierungssprache
* `<b> ... </b>` fett-gedruckter Text (inline) * `<b> ... </b>` fett-gedruckter Text (inline)
* `<a> ... </a>` Hyperlink (Verlinkung zu anderen Webseiten, Bildern) * `<a> ... </a>` Hyperlink (Verlinkung zu anderen Webseiten, Bildern)
* `<body> ... </body>` Sichtbarer Teil eines HTML-Dokuments * `<body> ... </body>` Sichtbarer Teil eines HTML-Dokuments
* `<details> ... </details>` Collapsable / Akkordion / Aufklapp-Text * `<details> ... </details>` Aufklapp-Text (Akkordion)
* **Selbst-schließend** * **Selbst-schließend**
* `<img />` Bild, Vektorgrafik etc. * `<img />` Bild, Vektorgrafik etc.
* `<input />` Eingabefeld * `<input />` Eingabefeld
@@ -1322,7 +1385,7 @@ a = anchor (Anker)
# Natives HTML # Natives HTML
Was noch üblicherweise mit Skriptsprachen wie JavaScript und anderen umgesetzt werden musste, kann nun HTML selber. Wer dieses dennoch verwendet, hat das einen sog. *"Code smell"*. Vieles, was früher JavaScript erforderte, beherrscht HTML inzwischen selbst. Wer es dennoch per Skript löst, produziert einen sogenannten *"Code Smell"*.
| Funktion | HTML-Element | | Funktion | HTML-Element |
|----------|--------------| |----------|--------------|
@@ -1447,7 +1510,7 @@ Accessibility ist eingebaut
<!-- _class: lead --> <!-- _class: lead -->
# Wie nutzen Menschen das Web? # Wie Menschen das Web nutzen
--- ---
@@ -1491,7 +1554,7 @@ Accessibility ist eingebaut
--- ---
# Wie nutzen Menschen das Web? # Wie Menschen das Web nutzen
| Eingabe | Nutzungsweise | | Eingabe | Nutzungsweise |
|---------|---------------| |---------|---------------|
@@ -1523,10 +1586,9 @@ PRÜFUNGSRELEVANT: Arten von Einschränkungen, Screenreader-Beispiele, WCAG
--- ---
<!-- _class: "erklaerung klausur" --> <!-- _class: erklaerung -->
<!-- _header: '' --> <!-- _header: '' -->
<!-- _footer: '' --> <!-- _footer: '' -->
<!-- _backgroundColor: #fce4ec -->
# Web-Zugänglichkeit – Vertiefung # Web-Zugänglichkeit – Vertiefung
@@ -1551,10 +1613,10 @@ Screenreader lesen den DOM sequenziell – semantisches HTML (`<nav>`, `<main>`,
--- ---
# Warum "Barrierefreiheit"? # Bedeutung von "Barrierefreiheit"
**Rechtlich:** **Rechtlich:**
- EU: European Accessibility Act (seit Juni 2025 in Kraft!) - EU: European Accessibility Act (seit Juni 2025 in Kraft)
- DE: BITV 2.0 (Behörden), Privatwirtschaft betroffen - DE: BITV 2.0 (Behörden), Privatwirtschaft betroffen
**Ethisch:** **Ethisch:**
@@ -1686,7 +1748,7 @@ button:focus-visible {
border-radius: 4px; border-radius: 4px;
} }
/* Anti-Pattern: Tastatur-Nutzer sind "blind" */ /* Anti-Pattern: Tastatur-Nutzende sind "blind" */
button:focus { outline: none; } button:focus { outline: none; }
``` ```
@@ -1826,7 +1888,7 @@ Fokus-Indikator immer sichtbar? Logische Reihenfolge? Alle Elemente erreichbar?
<!-- <!--
WAVE Tab-Reihenfolge: visualisiert DOM-Reihenfolge als Tab-Pfad. WAVE Tab-Reihenfolge: visualisiert DOM-Reihenfolge als Tab-Pfad.
Wenn Pfad kreuz und quer → schlechte Struktur für Tastatur-/Screenreader-Nutzer. Wenn Pfad kreuz und quer → schlechte Struktur für Tastatur-/Screenreader-Nutzende.
--> -->
--- ---
File diff suppressed because it is too large. Load diff
+26 -27
View File
@@ -98,7 +98,7 @@ Hochschule der Medien Stuttgart
<!-- _class: lead --> <!-- _class: lead -->
# Kapitel 3 – 24.01.2026 # Kapitel 3
## Interaktivität & JavaScript ## Interaktivität & JavaScript
--- ---
@@ -119,16 +119,15 @@ Hochschule der Medien Stuttgart
--- ---
# Was ist JavaScript? # JavaScript im Überblick
**Die einzige Programmiersprache, die im Browser läuft!** JavaScript ist die einzige Programmiersprache, die nativ im Browser läuft.
- **1995:** In 10 Tagen entwickelt (Brendan Eich, Netscape) - **1995:** In 10 Tagen entwickelt (Brendan Eich, Netscape)
- **Heute:** Überall – Browser, Server, Apps, IoT - **Heute:** Überall – Browser, Server, Apps, IoT
**Nicht verwechseln:** **Nicht verwechseln:**
- Java ≠ JavaScript (komplett unterschiedlich!) - Java und JavaScript sind zwei völlig verschiedene Sprachen.
- "JavaScript ist zu Java wie Carpet zu Car"
<!-- <!--
Ursprünglich "Mocha", dann "LiveScript", dann "JavaScript" (Marketing!) Ursprünglich "Mocha", dann "LiveScript", dann "JavaScript" (Marketing!)
@@ -159,7 +158,7 @@ ES6 = großes Update mit vielen Features
# JavaScript einbinden # JavaScript einbinden
**Option 1: Externe Datei (empfohlen!)** **Option 1: Externe Datei (empfohlen)**
```html ```html
<body> <body>
<!-- Inhalt --> <!-- Inhalt -->
@@ -174,7 +173,7 @@ ES6 = großes Update mit vielen Features
</script> </script>
``` ```
⚠️ Script am **Ende** des `<body>` – damit HTML zuerst lädt! **Wichtig:** Das Script gehört ans **Ende** des `<body>`, damit das HTML zuerst lädt.
<!-- <!--
Externe Datei = wiederverwendbar, cachebar Externe Datei = wiederverwendbar, cachebar
@@ -184,7 +183,7 @@ Oder: defer-Attribut
--- ---
# Die Konsole: Euer bester Freund # Die JavaScript-Konsole
**Browser DevTools → Console** **Browser DevTools → Console**
- Windows/Linux: `F12` oder `Strg+Shift+I` - Windows/Linux: `F12` oder `Strg+Shift+I`
@@ -196,7 +195,7 @@ console.log(42);
console.log("Die Antwort ist:", 42); console.log("Die Antwort ist:", 42);
``` ```
**Direkt in der Konsole tippen und Enter!** Befehle lassen sich direkt in der Konsole eingeben und mit Enter ausführen.
<!-- <!--
Console = REPL (Read-Eval-Print-Loop) Console = REPL (Read-Eval-Print-Loop)
@@ -348,20 +347,20 @@ true || false // true (ODER)
**In der Browser-Konsole:** **In der Browser-Konsole:**
1. Erstellen Sie Variablen für sich: 1. Erstellt Variablen für euch:
```javascript ```javascript
const name = "Your Name"; const name = "Your Name";
let age = 20; let age = 20;
const hobbies = ["Hobby1", "Hobby2"]; const hobbies = ["Hobby1", "Hobby2"];
``` ```
2. Gib sie aus: 2. Gebt sie aus:
```javascript ```javascript
console.log(`I am ${name}, ${age} years old.`); console.log(`I am ${name}, ${age} years old.`);
console.log("My hobbies:", hobbies); console.log("My hobbies:", hobbies);
``` ```
3. Erstelle ein `person`-Objekt und greife auf Properties zu 3. Erstellt ein `person`-Objekt und greift auf Properties zu
--- ---
@@ -491,7 +490,7 @@ Funktionale Programmierung, Original-Array bleibt unverändert
--- ---
# Was ist das DOM? # Das DOM im Überblick
**D**ocument **O**bject **M**odel **D**ocument **O**bject **M**odel
@@ -512,7 +511,7 @@ Jedes Element ist ein Objekt
# Elemente finden # Elemente finden
```javascript ```javascript
// Einzelnes Element (erstes gefundene) // Einzelnes Element (erstes gefundenes)
document.getElementById("meinId"); document.getElementById("meinId");
document.querySelector(".meineKlasse"); document.querySelector(".meineKlasse");
document.querySelector("nav > a"); document.querySelector("nav > a");
@@ -632,10 +631,10 @@ remove = aus DOM entfernen
**Aufgaben (in script.js):** **Aufgaben (in script.js):**
1. Ändere den Text von `#output` 1. Ändert den Text von `#output`
2. Füge 5 `<li>`-Elemente zur Liste hinzu (mit Schleife!) 2. Fügt 5 `<li>`-Elemente zur Liste hinzu (mit Schleife)
3. Gib jedem geraden Element eine Klasse `highlight` 3. Gebt jedem geraden Element eine Klasse `highlight`
4. Style `.highlight` in CSS (z.B. andere Farbe) 4. Gestaltet `.highlight` in CSS (z. B. andere Farbe)
--- ---
@@ -646,7 +645,7 @@ remove = aus DOM entfernen
--- ---
# Was sind Events? # Events im Überblick
**NutzerInnen interagieren mit der Seite:** **NutzerInnen interagieren mit der Seite:**
- Klicken - Klicken
@@ -654,7 +653,7 @@ remove = aus DOM entfernen
- Scrollen - Scrollen
- Formulare absenden - Formulare absenden
**JavaScript kann darauf reagieren!** JavaScript kann auf diese Ereignisse reagieren.
<!-- <!--
Event = Ereignis Event = Ereignis
@@ -784,9 +783,9 @@ toggleButton.addEventListener("click", () => {
// Button-Text ändern // Button-Text ändern
if (body.classList.contains("dark-mode")) { if (body.classList.contains("dark-mode")) {
toggleButton.textContent = "☀️ Light Mode"; toggleButton.textContent = "Light Mode";
} else { } else {
toggleButton.textContent = "🌙 Dark Mode"; toggleButton.textContent = "Dark Mode";
} }
}); });
``` ```
@@ -1002,21 +1001,21 @@ Frameworks für größere Apps, Komponenten-basiert
--- ---
# Ihr könnt jetzt... # Das könnt ihr jetzt
- Den historischen Kontext von IT verstehen - Den historischen Kontext von IT verstehen
- Erklären wie das Internet funktioniert - Erklären wie das Internet funktioniert
- HTML-Seiten strukturieren - HTML-Seiten strukturieren
- CSS für Layout und Design nutzen - CSS für Layout und Design nutzen
- JavaScript für Interaktivität einsetzen - JavaScript für Interaktivität einsetzen
- **Mit EntwicklerInnen auf Augenhöhe sprechen!** - **Mit EntwicklerInnen auf Augenhöhe sprechen**
--- ---
# Feedback – Eure Meinung zählt! # Feedback
Euer ehrliches Feedback hilft mir, den Kurs zu verbessern. Euer ehrliches Feedback hilft mir, den Kurs zu verbessern.
**Es gibt keine falschen Antworten!** Es gibt keine falschen Antworten.
- Was hat gut funktioniert? - Was hat gut funktioniert?
- Was könnte besser sein? - Was könnte besser sein?
@@ -1028,7 +1027,7 @@ Euer ehrliches Feedback hilft mir, den Kurs zu verbessern.
<!-- _class: lead --> <!-- _class: lead -->
# Danke! # Danke
**Kontakt:** lb-czechowski@hdm-stuttgart.de **Kontakt:** lb-czechowski@hdm-stuttgart.de
@@ -6,47 +6,70 @@
<style> <style>
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, sans-serif; } html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, sans-serif; }
body { padding: 30px 40px; display: inline-block; } body { padding: 30px 40px; display: inline-block; }
.wrap { display: flex; gap: 36px; align-items: flex-start; } .headers {
.col { display: flex; flex-direction: column; gap: 4px; } display: grid;
h3 { margin: 0 0 10px; font-size: 1.1rem; color: #1a1a2e; text-align: center; } grid-template-columns: 240px 240px;
column-gap: 36px;
margin-bottom: 10px;
}
.headers h3 { margin: 0; font-size: 1.1rem; color: #1a1a2e; text-align: center; }
.grid {
display: grid;
grid-template-columns: 240px 240px;
grid-template-rows: repeat(7, 48px);
column-gap: 36px;
row-gap: 6px;
}
.box { .box {
border: 2px solid #4a4a6a; border: 2px solid #4a4a6a;
padding: 10px 20px; border-radius: 4px;
min-width: 230px;
font-size: 0.95rem;
background: #f3f4f6;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 4px; font-size: 0.95rem;
text-align: center;
padding: 0 12px;
} }
.osi .box { background: #dbeafe; border-color: #1e40af; color: #1e3a8a; } .osi {
.tcpip .box { background: #fce7d6; border-color: #c2410c; color: #7c2d12; font-weight: 600; } background: #dbeafe;
.tcpip .app { min-height: 126px; } border-color: #1e40af;
.tcpip .trans { min-height: 40px; } color: #1e3a8a;
.tcpip .net { min-height: 40px; } }
.tcpip .link { min-height: 82px; } .tcpip {
background: #fce7d6;
border-color: #c2410c;
color: #7c2d12;
font-weight: 600;
}
/* OSI: column 1, one box per row */
.o7 { grid-area: 1 / 1; } .o6 { grid-area: 2 / 1; } .o5 { grid-area: 3 / 1; }
.o4 { grid-area: 4 / 1; } .o3 { grid-area: 5 / 1; } .o2 { grid-area: 6 / 1; }
.o1 { grid-area: 7 / 1; }
/* TCP/IP: column 2, each block spans its OSI equivalents */
.t-app { grid-area: 1 / 2 / 4 / 2; } /* OSI 5-7 */
.t-tra { grid-area: 4 / 2 / 5 / 2; } /* OSI 4 */
.t-net { grid-area: 5 / 2 / 6 / 2; } /* OSI 3 */
.t-link { grid-area: 6 / 2 / 8 / 2; } /* OSI 1-2 */
</style> </style>
</head> </head>
<body> <body>
<div class="wrap"> <div class="headers">
<div class="col osi"> <h3>OSI (7 Schichten)</h3>
<h3>OSI (7 Schichten)</h3> <h3>TCP/IP (4 Schichten)</h3>
<div class="box">7. Anwendung</div> </div>
<div class="box">6. Darstellung</div> <div class="grid">
<div class="box">5. Sitzung</div> <div class="box osi o7">7. Anwendung</div>
<div class="box">4. Transport</div> <div class="box osi o6">6. Darstellung</div>
<div class="box">3. Vermittlung</div> <div class="box osi o5">5. Sitzung</div>
<div class="box">2. Sicherung</div> <div class="box osi o4">4. Transport</div>
<div class="box">1. Bitübertragung</div> <div class="box osi o3">3. Vermittlung</div>
</div> <div class="box osi o2">2. Sicherung</div>
<div class="col tcpip"> <div class="box osi o1">1. Bitübertragung</div>
<h3>TCP/IP (4 Schichten)</h3>
<div class="box app">Anwendung</div> <div class="box tcpip t-app">Anwendung</div>
<div class="box trans">Transport</div> <div class="box tcpip t-tra">Transport</div>
<div class="box net">Internet</div> <div class="box tcpip t-net">Internet</div>
<div class="box link">Netzzugang</div> <div class="box tcpip t-link">Netzzugang</div>
</div>
</div> </div>
</body> </body>
</html> </html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 63 KiB

+16 -11
View File
@@ -5,18 +5,17 @@
<title>Port-Highlight</title> <title>Port-Highlight</title>
<style> <style>
html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, sans-serif; } html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, sans-serif; }
body { padding: 28px 40px; display: inline-block; } body { padding: 28px 40px 110px; display: inline-block; }
.line { .line {
font-family: ui-monospace, "SF Mono", monospace; font-family: ui-monospace, "SF Mono", monospace;
font-size: 2.2rem; font-size: 2.2rem;
color: #1a1a2e; color: #1a1a2e;
display: flex; display: flex;
align-items: center; align-items: baseline;
} }
.port-wrap { .port-wrap {
display: inline-flex; position: relative;
flex-direction: column; display: inline-block;
align-items: center;
margin-left: 4px; margin-left: 4px;
} }
.port { .port {
@@ -26,11 +25,20 @@
border-radius: 4px; border-radius: 4px;
font-weight: 700; font-weight: 700;
} }
.below {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
margin-top: 4px;
}
.connector { .connector {
width: 2px; width: 2px;
height: 18px; height: 18px;
background: #c2410c; background: #c2410c;
margin-top: 4px;
} }
.caption-text { .caption-text {
background: #fed7aa; background: #fed7aa;
@@ -42,16 +50,13 @@
font-size: 1rem; font-size: 1rem;
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
margin-top: 2px; margin-top: 2px;
white-space: nowrap;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="line"> <div class="line">
212.132.79.37:<div class="port-wrap"> 212.132.79.37:<span class="port-wrap"><span class="port">443</span><span class="below"><span class="connector"></span><span class="caption-text">Port 443 = HTTPS</span></span></span>
<span class="port">443</span>
<div class="connector"></div>
<div class="caption-text">Port 443 = HTTPS</div>
</div>
</div> </div>
</body> </body>
</html> </html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

@@ -104,7 +104,7 @@
<!-- SYN-ACK --> <!-- SYN-ACK -->
<div class="msg arrow-left msg-reverse" style="left: 83px; right: 80px; top: 180px;"> <div class="msg arrow-left msg-reverse" style="left: 83px; right: 80px; top: 180px;">
<div class="label reply" style="left: 50%; transform: translateX(-50%);">SYN-ACK · Seq=5000, Ack=1001</div> <div class="label reply" style="left: 50%; transform: translateX(-50%);">SYN-ACK · Seq=5000, Ack=1001</div>
<div class="quote" style="left: 50%; transform: translateX(-50%);">"OK, ich starte bei 5000"</div> <div class="quote" style="left: 50%; transform: translateX(-50%);">"OK, ich starte bei 5000 und erwarte 1001"</div>
</div> </div>
<!-- ACK --> <!-- ACK -->
@@ -113,7 +113,7 @@
<div class="quote" style="left: 50%; transform: translateX(-50%);">"Verstanden, los geht's"</div> <div class="quote" style="left: 50%; transform: translateX(-50%);">"Verstanden, los geht's"</div>
</div> </div>
<div class="final">✓ Verbindung steht</div> <div class="final">Verbindung steht</div>
</div> </div>
</body> </body>
</html> </html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 63 KiB

+94 -411
View File
@@ -5,7 +5,7 @@ paginate: true
backgroundColor: #fff backgroundColor: #fff
header: "Grundlagen IT- und Internettechnik (223015c)" header: "Grundlagen IT- und Internettechnik (223015c)"
footer: "Michael Czechowski – HdM Stuttgart – SoSe 2026" footer: "Michael Czechowski – HdM Stuttgart – SoSe 2026"
title: "Kapitel 1: Geschichte, Grundlagen & HTML" title: Grundlagen IT- und Internettechnik
--- ---
<style> <style>
:root { :root {
@@ -20,13 +20,13 @@ section {
font-size: 1.7rem; font-size: 1.7rem;
} }
h1 { h1 {
color: #a02060; /* darker raspberry */ color: #a02060;
} }
section.invert h1 { section.invert h1 {
color: #fff; color: #fff;
} }
h2 { h2 {
color: #1f2937; /* dark gray, almost black */ color: #1f2937;
} }
pre { pre {
background: #0f0f23; background: #0f0f23;
@@ -82,10 +82,9 @@ section.aufgabe footer {
<!-- _class: invert --> <!-- _class: invert -->
<!-- _header: '' --> <!-- _header: '' -->
<!-- _footer: '' --> <!-- _backgroundColor: #000 -->
<!-- _backgroundColor: black -->
![bg fit opacity:0.2](./assets/background-termin-2.png) ![bg cover opacity:0.2](./assets/background-termin-1.png)
# Grundlagen IT- und Internettechnik # Grundlagen IT- und Internettechnik
@@ -103,39 +102,6 @@ Hochschule der Medien Stuttgart
<!-- _footer: "" --> <!-- _footer: "" -->
# Die 5 Komponenten
| Komponente | Funktion |
|------------|----------|
| **Rechenwerk (ALU)** | Führt Berechnungen durch |
| **Steuerwerk** | Interpretiert Befehle, steuert Ablauf |
| **Speicherwerk** | Speichert Programme UND Daten |
| **Ein-/Ausgabe** | Tastatur, Bildschirm, Netzwerk |
| **Bus-System** | Verbindet alle Komponenten |
**Wichtig:** Programme und Daten **gemeinsam** im Speicher!
<!--
VON-NEUMANN-ARCHITEKTUR (1945): Grundlage aller modernen Computer
5 KOMPONENTEN:
- ALU (Arithmetic Logic Unit): Rechnet (+, -, ×, ÷) und vergleicht (>, <, =)
- Steuerwerk: Holt Befehle, dekodiert sie, steuert Ausführung (Fetch-Decode-Execute)
- Speicherwerk: RAM (flüchtig) + ROM (permanent), enthält Code UND Daten
- Ein-/Ausgabe (I/O): Tastatur, Maus, Bildschirm, Netzwerk, USB, Sensoren
- Bus-System: Adressbus (wo), Datenbus (was), Steuerbus (wie)
KERNPRINZIP: Stored Program Concept - Programme im selben Speicher wie Daten
VORHER (z.B. ENIAC): Programme durch Umstecken von Kabeln, tagelange Arbeit
NACHHER: Programme als austauschbare Daten → Flexibilität, Software-Industrie möglich
PRÜFUNGSRELEVANT: 5 Komponenten benennen und erklären können, Stored Program Concept
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Von-Neumann-Architektur: Bedeutung # Von-Neumann-Architektur: Bedeutung
**Ohne Von-Neumann-Architektur:** **Ohne Von-Neumann-Architektur:**
@@ -173,117 +139,18 @@ PRÜFUNGSRELEVANT: Warum Von-Neumann revolutionär, Unterschied zu Harvard, Beis
<!-- _footer: "" --> <!-- _footer: "" -->
# HTML Metadaten # HTML-Anatomie (2/2)
```html
<html>
<head>
<title>Im Browsertab als Überschrift sichtbar</title>
<meta name="description" content="" />
<meta name="og:image" content="https://...." />
</head>
...
</html>
```
- <small>*Webseiten werden nicht nur von Menschen besucht, sondern auch von Suchmaschinen, Programmen, Bots etc.*</small>
- <small>*Metadaten helfen bspw. auch Screen-Readern beim "Verstehen" der Inhalte*</small>
<!--
HEAD-BEREICH: Metadaten, nicht sichtbar im Browser-Fenster
WICHTIGE META-TAGS:
- <title>: Browser-Tab, Lesezeichen, Suchergebnis-Titel
- <meta name="description">: Suchergebnis-Snippet (max. 160 Zeichen)
- <meta name="viewport">: Mobile Darstellung (width=device-width)
- <meta charset="UTF-8">: Zeichenkodierung (Umlaute!)
OPEN GRAPH (og:*): Social Media Vorschau (Facebook, LinkedIn, WhatsApp)
- og:title, og:description, og:image, og:url
TWITTER CARDS: twitter:card, twitter:title, twitter:image
SEO-RELEVANZ: Google nutzt title + description für Ranking und Snippet
ACCESSIBILITY: <html lang="de"> für Screenreader-Aussprache
PRÜFUNGSRELEVANT: Was gehört in <head>, Unterschied zu <body>, wichtigste Meta-Tags
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Wie nutzen Menschen das Web?
| Eingabe | Nutzungsweise |
|---------|---------------|
| **Maus** | Klicken, Scrollen |
| **Tastatur** | Tab-Navigation, Enter, Pfeiltasten |
| **Screenreader** | Vorlesen von Inhalten (NVDA, VoiceOver, JAWS) |
| **Sprachsteuerung** | "Klicke auf Anmelden" |
| **Augensteuerung** | Eye-Tracking |
| **Switch-Geräte** | Ein-/Aus-Schalter |
→ **Nicht alle nutzen Maus + Bildschirm!**
<!--
ACCESSIBILITY (a11y): a + 11 Buchstaben + y
STATISTIK: ~15% der Weltbevölkerung haben eine Behinderung (WHO)
ARTEN VON EINSCHRÄNKUNGEN:
- Permanent: Blindheit, Taubheit, motorische Einschränkungen
- Temporär: Gebrochener Arm, Augen-OP, Ohrenentzündung
- Situativ: Helle Sonne (Kontrast), laute Umgebung (kein Audio), Baby auf Arm (eine Hand)
ASSISTIVE TECHNOLOGIEN:
- Screenreader: NVDA (Windows, kostenlos), VoiceOver (Apple), JAWS (kommerziell)
- Braillezeilen: Taktile Ausgabe für Blinde
- Switch-Geräte: Für motorische Einschränkungen
- Spracheingabe: Dragon NaturallySpeaking, Siri, Google Assistant
WCAG: Web Content Accessibility Guidelines (A, AA, AAA)
EUROPEAN ACCESSIBILITY ACT: Seit Juni 2025 verpflichtend!
PRÜFUNGSRELEVANT: Arten von Einschränkungen, Screenreader-Beispiele, WCAG
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Warum "Barrierefreiheit"?
**Rechtlich:**
- EU: European Accessibility Act (seit Juni 2025 in Kraft!)
- DE: BITV 2.0 (Behörden), Privatwirtschaft betroffen
**Ethisch:**
- Teilhabe für alle Menschen
- Digitale Inklusion
**Praktisch:**
- Bessere UX für **alle** (SEO, Mobile, ältere Menschen)
- Größerer Markt (~15% der Bevölkerung)
<!--
RECHTLICHER RAHMEN:
- European Accessibility Act (EAA): EU-Richtlinie, seit 28. Juni 2025 in Kraft
- Betrifft: E-Commerce, Banking, Telekommunikation, Transport, E-Books
- BITV 2.0: Barrierefreie Informationstechnik-Verordnung (DE, öffentliche Stellen)
- BFSG: Barrierefreiheitsstärkungsgesetz (DE-Umsetzung des EAA)
- Strafen: Bis zu 100.000€ Bußgeld möglich
CURB-CUT-EFFEKT:
- Bordsteinabsenkung für Rollstuhlfahrer → hilft auch Kinderwagen, Rollkoffern, Fahrrädern
- Untertitel für Gehörlose → helfen in lauter Umgebung, beim Sprachlernen
- Kontrastreiche Farben → besser bei Sonnenlicht, für ältere Menschen
BUSINESS CASE:
- 15% Zielgruppe (Menschen mit Behinderung)
- +20% ältere Menschen (Sehschwäche, motorische Einschränkungen)
- SEO-Vorteile: Alt-Texte, semantisches HTML = besser für Google
PRÜFUNGSRELEVANT: EAA kennen, Curb-Cut-Effekt erklären können, Business Case
-->
- Wir unterscheiden zwischen **umschließende** und **selbst-schließende** HTML-Tags
- **Umschließend**
- `<b> ... </b>` fett-gedruckter Text (inline)
- `<a> ... </a>` Hyperlink (Verlinkung zu anderen Webseiten, Bildern)
- `<body> ... </body>` Sichtbarer Teil eines HTML-Dokuments
- `<details> ... </details>` Aufklapp-Text (Akkordion)
- **Selbst-schließend**
- `<img />` Bild, Vektorgrafik etc.
- `<input />` Eingabefeld
- `<meta />` Metadaten
--- ---
@@ -294,17 +161,16 @@ PRÜFUNGSRELEVANT: EAA kennen, Curb-Cut-Effekt erklären können, Business Case
# Barrieren im Netz vermeiden (a11y) # Barrieren im Netz vermeiden (a11y)
**Tastatur-Test:** **Tastatur-Test:**
- Alle Funktionen nur mit Tab + Enter nutzbar? - Alle Funktionen sind nur mit Tab + Enter nutzbar
- Fokus immer sichtbar? - Fokus immer sichtbar: Textlinks, Buttons, Texteingabe etc.
- Logische Tab-Reihenfolge? - Logische Tab-Reihenfolge: "Skip to main", Navigation, Hauptinhalt
**Screenreader-Test:** **Screenreader-Test:**
- VoiceOver (Mac): `Cmd + F5` - VoiceOver (Mac): `Cmd + F5`
- NVDA (Windows): Gratis-Download - NVDA (Windows): Gratis-Download
**Tools:** **Tools/Browser-Extensions:**
- axe DevTools, WAVE (Browser-Extensions) - [WAVE](https://wave.webaim.org/), [axe DevTools](https://www.deque.com/axe/devtools/), [Web Disability Simulator](https://chromewebstore.google.com/detail/web-disability-simulator/olioanlbgbpmdlgjnnampnnlohigkjla)
- Lighthouse (in Chrome DevTools)
<!-- <!--
Automatische Tests finden ~30% der Probleme Automatische Tests finden ~30% der Probleme
@@ -319,168 +185,19 @@ Echte NutzerInnen einbeziehen = Gold-Standard
<!-- _footer: "" --> <!-- _footer: "" -->
# Das TCP/IP-Modell
| Schicht | Name | Aufgabe | Protokolle |
|---------|------|---------|------------|
| **4** | Anwendung | Was? Welcher Dienst? | HTTP, DNS, SMTP |
| **3** | Transport | Zuverlässigkeit, Ports | TCP, UDP |
| **2** | Internet | Routing, globale Adressen | IP |
| **1** | Netzzugang | Lokale Übertragung | Ethernet, WLAN |
**4 Schichten. Das reicht, um das Internet zu verstehen.**
<!--
SPEAKER NOTES:
- TCP/IP = das echte Internet
- 4 Schichten:
- Anwendung: Was will ich? (HTTP, DNS, Mail)
- Transport: Kommt an? Welches Programm? (TCP, UDP)
- Internet: Wie zum Zielrechner? (IP)
- Netzzugang: Wie zum nächsten Gerät? (Ethernet, WLAN)
- KLAUSUR: Welches Protokoll → welche Schicht
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Dateneinheiten pro Schicht
| Schicht | Name | Dateneinheit | Was kommt hinzu? |
|---------|------|--------------|------------------|
| Anwendung | HTTP, DNS | **Daten** | – |
| Transport | TCP, UDP | **Segment** | Ports, Sequenznummern |
| Internet | IP | **Paket** | IP-Adressen |
| Netzzugang | Ethernet | **Frame** | MAC-Adressen, Prüfsumme |
Merkhilfe: **D**aten → **S**egment → **P**aket → **F**rame
(„**D**er **S**ache **P**raktischer **F**olgen")
<!--
SPEAKER NOTES:
- Glossar-Begriffe jetzt eingeordnet:
- Anwendung: Daten/Message
- Transport: Segment
- Internet: Paket
- Netzzugang: Frame
- Merkhilfe: D-S-P-F (eigene erfinden!)
- KLAUSUR: Begriff → Schicht
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# IP vs. MAC vs. Port
| | IP-Adresse | MAC-Adresse | Port |
|---|-----------|-------------|------|
| **Frage** | Welcher Rechner? | Welches Gerät nebenan? | Welches Programm? |
| **Reichweite** | Global | Lokal (1 Hop) | Auf einem Rechner |
| **Ändert sich?** | Nein | Ja, bei jedem Hop | Nein |
| **Schicht** | Internet (IP) | Netzzugang (Ethernet) | Transport (TCP/UDP) |
| **Beispiel** | 212.132.79.37 | aa:bb:cc:dd:ee | 443 |
<!--
SPEAKER NOTES:
- KLAUSUR: Diese Tabelle
- IP: Welcher Rechner? Global. Bleibt gleich.
- MAC: Welcher Nachbar? Lokal. Ändert sich.
- Port: Welches Programm? Bleibt gleich.
- Merken: NUR MAC ändert sich unterwegs
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Der 3-Way-Handshake
```
Client (dein Laptop) Server
| |
|-------- SYN (Seq=1000) --------------->|
| "Ich will reden" |
| |
|<--- SYN-ACK (Seq=5000, Ack=1001) ------|
| "OK, ich auch. Ich starte bei 5000"|
| |
|-------- ACK (Ack=5001) --------------->|
| "Verstanden, los geht's" |
| |
| [ Verbindung steht ] |
```
**SYN** = Synchronize · **ACK** = Acknowledge
<!--
SPEAKER NOTES:
- 3 Pakete → Verbindung steht
- 1. Client SYN: "Will Verbindung, starte bei 1000"
- 2. Server SYN-ACK: "OK, ich starte bei 5000, erwarte 1001"
- 3. Client ACK: "Verstanden, erwarte 5001"
- Seq-Nr wichtig: Beide wissen nächstes erwartetes Byte
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# TCP vs. UDP
| | TCP | UDP |
|---|-----|-----|
| **Verbindung** | Ja (Handshake) | Nein |
| **Reihenfolge** | Garantiert | Nicht garantiert |
| **Verlorene Pakete** | Werden nachgefordert | Gehen verloren |
| **Overhead** | Höher | Niedriger |
| **Use Cases** | Web, Email, Downloads | Video-Calls, Gaming, DNS |
<!--
SPEAKER NOTES:
- Zwei Transport-Protokolle
- TCP: zuverlässig
- Handshake, Seq-Nr, Bestätigungen, Neuübertragung
- Perfekt für Web, Downloads (nichts darf fehlen)
- UDP: schneller
- Kein Handshake, keine Bestätigungen
- Verloren = weg
- Perfekt für Video-Calls (Verzögerung schlimmer als Verlust)
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# HTTP-Methoden # HTTP-Methoden
| Methode | Bedeutung | Beispiel | HTTP-Methoden beschreiben, **was** der Client mit einer Ressource tun will.
|---------|-----------|----------|
| **GET** | Daten abrufen | Seite laden | | Methode | Bedeutung | Beispiel |
| **POST** | Daten senden | Formular absenden | |---------|-----------|-----------------------------------------------|
| **PUT** | Daten ersetzen | Profil aktualisieren | | **GET** | Daten abrufen | Seite laden `GET /index.html HTTP/2.0` |
| **DELETE** | Daten löschen | Account löschen | | **POST** | Daten senden | Formular absenden `POST /login HTTP/2.0` |
| **PUT** | Daten ersetzen | Profil aktualisieren `PUT /users/42 HTTP/2.0` |
| **DELETE** | Daten löschen | Account löschen `DELETE /users/42 HTTP/2.0` |
<small>CRUD = Create, Read, Update, Delete</small>
```http
GET /index.html HTTP/1.1
POST /login HTTP/1.1
```
<!-- <!--
SPEAKER NOTES: SPEAKER NOTES:
@@ -505,10 +222,12 @@ Das wird wichtiger, wenn ihr mit REST-APIs arbeitet.
# HTTP Status-Codes # HTTP Status-Codes
Jede HTTP-Response enthält einen dreistelligen Status-Code — die erste Ziffer verrät die Kategorie.
```http ```http
HTTP/1.1 200 OK HTTP/2.0 200 OK
HTTP/1.1 404 Not Found HTTP/2.0 404 Not Found
HTTP/1.1 500 Internal Server Error HTTP/2.0 500 Internal Server Error
``` ```
| Code-Bereich | Bedeutung | | Code-Bereich | Bedeutung |
@@ -539,112 +258,76 @@ Status-Codes sagen euch, was passiert ist.
<!-- _footer: "" --> <!-- _footer: "" -->
# Zusammenfassung Teil 1 # Selektoren: ID
**Der Ablauf:** ![bg right:35%](./assets/demos/css-selector-id.png)
1. DNS (UDP:53) → Name zu IP
2. TCP-Handshake (SYN → SYN-ACK → ACK)
3. HTTP-Request (GET /...)
4. HTTP-Response (200 OK + HTML)
**Die Konzepte:** ```html
- 4 Schichten: Anwendung → Transport → Internet → Netzzugang <nav id="hauptnavigation">...</nav>
- IP bleibt gleich, MAC ändert sich pro Hop
- Ports identifizieren Programme
- Encapsulation: Jede Schicht verpackt die darüberliegende
<!--
SPEAKER NOTES:
Das ist die Zusammenfassung für Teil 1. Das solltet ihr können:
Den Ablauf: DNS, TCP-Handshake, HTTP-Request, HTTP-Response.
Die Schichten und ihre Protokolle.
Den Unterschied zwischen IP und MAC – was bleibt gleich, was ändert sich.
Was Ports tun.
Wie Encapsulation funktioniert.
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Spezifität: Welche Regel gewinnt?
| Selektor | Spezifität |
|----------|------------|
| Element (`p`) | 0,0,0,1 |
| Klasse (`.wichtig`) | 0,0,1,0 |
| ID (`#header`) | 0,1,0,0 |
| Inline (`style="..."`) | 1,0,0,0 |
```css
p { color: blue; } /* 0,0,0,1 */
.text { color: green; } /* 0,0,1,0 → gewinnt */
#intro { color: red; } /* 0,1,0,0 → gewinnt über beide */
``` ```
<!--
SPEAKER NOTES:
Wenn mehrere Regeln auf ein Element zutreffen, gewinnt die spezifischere.
Spezifität wird in vier Zahlen gemessen. Von links nach rechts: Inline, IDs, Klassen, Elemente.
Ein Element-Selektor hat 0,0,0,1. Eine Klasse hat 0,0,1,0. Eine ID hat 0,1,0,0.
0,0,1,0 schlägt 0,0,0,99 – auch hundert Element-Selektoren schlagen keine Klasse.
Inline-Styles schlagen alles – außer !important, was ihr vermeiden solltet.
-->
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Responsive Design
```css ```css
/* Mobile First: Basis-Styles */ #hauptnavigation {
.container { background: #333;
padding: 1rem; padding: 1rem;
background: white;
}
/* Ab 768px (Tablet): Anpassungen */
@media (min-width: 768px) {
.container {
padding: 2rem;
background: red;
}
}
/* Ab 1024px (Desktop): Weitere Anpassungen */
@media (min-width: 1024px) {
.container {
background: green;
}
} }
``` ```
**Raute** vor dem Namen = ID
**Achtung:** IDs sollten **einmalig** pro Seite sein.
<!-- <!--
SPEAKER NOTES: SPEAKER NOTES:
Responsive Design: Die Seite passt sich der Bildschirmgröße an. ID-Selektoren sind noch spezifischer. Raute vor dem Namen.
Mobile First: Ihr schreibt zuerst die Styles für kleine Screens. Dann fügt ihr mit @media-Queries Anpassungen für größere Screens hinzu. IDs sollten auf einer Seite nur einmal vorkommen. Für Navigation, Header, Footer, Main – einmalige Elemente.
min-width heißt: "ab dieser Breite". Also: Basis gilt für alle. Ab 768px gelten zusätzliche Regeln. Ab 1024px noch mehr. In der Praxis: Klassen sind meist besser. IDs haben hohe Spezifität und sind unflexibel.
-->
Das Gegenteil wäre Desktop First mit max-width – aber Mobile First ist heute Standard.
---
<!-- _header: "" -->
<!-- _footer: "" -->
# Farben
![bg right:35%](./assets/demos/css-colors.png)
CSS kennt fünf Farb-Notationen: **Keyword**, **Hex**, **RGB**, **RGBA** und **HSL**.
```css
/* Keyword */
color: red;
color: rebeccapurple;
/* Hex */
color: #FF0000;
color: #F00;
/* RGB = Red Green Blue, RGBA mit Alpha */
color: rgb(255, 0, 0);
color: rgba(255, 0, 0, 0.5);
/* HSL = Hue, Saturation, Lightness */
color: hsl(0, 100%, 50%);
```
MDN-Referenz: [CSS `<color>`](https://developer.mozilla.org/de/docs/Web/CSS/Reference/Values/color_value)
<!--
SPEAKER NOTES:
Farben können auf verschiedene Arten angegeben werden.
Keywords: red, blue, rebeccapurple. Begrenzte Auswahl, aber lesbar.
Hex: Sechs Zeichen, je zwei für Rot, Grün, Blau. #FF0000 ist reines Rot.
RGB: Gleiche Idee, aber dezimal. rgb(255, 0, 0).
RGBA: Mit Alpha-Kanal für Transparenz. 0 ist unsichtbar, 1 ist voll sichtbar.
HSL: Hue, Saturation, Lightness. Manchmal intuitiver für Farbanpassungen.
--> -->
File diff suppressed because it is too large. Load diff
+666
View File
@@ -0,0 +1,666 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: Intro
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
![bg cover opacity:0.2](./assets/background.png)
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026**
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
# Ressourcen zum Selbstlernen
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
---
<!-- _class: lead -->
# Herzlich Willkommen!
## 1. Sitzung: 08.05.2026
---
# Über mich
![bg right:33% fit](./assets/logos/collage.png)
**Michael Werner Czechowski**
- Systems and Platform Engineer
- Schwerpunkte:
- Web-Technologien, Barriere-Armut, Open Source
- Hintergrund:
- Philosophie (Uni Stuttgart)
- Wirtschaftsinformatik (Leibniz-FH Hannover)
- Honorardozent (DHBW Stuttgart, HdM Stuttgart, Leibniz-FH)
- Kontakt: michael.czechowski@lehre.dhbw-stuttgart.de
---
# Vorstellungsrunde
## Wer bist du, was machst du?
> Ich bin \_\_\_, mache bei \_\_\_ \_\_\_ und ein Fun Fact über mich ist, dass ich ...
- ... sämtliche One Piece Bände gelesen habe.
- ... alle XKCD Comics mit Titel kenne.
- ... irgendwann bei \_\_\_ arbeiten möchte.
- ... Paulaner Spezi im Blindtest herausschmecken kann.
---
# Vorlesungsplan
<style scoped>
.lp-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-top: 0.5rem;
}
.lp-card {
border: 1px solid #d63384;
border-radius: 8px;
padding: 10px 12px;
background: #fff;
font-size: 0.95em;
display: flex;
flex-direction: column;
gap: 4px;
}
.lp-card .head {
font-weight: bold;
color: #a02060;
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 0.85em;
}
.lp-card .head .num {
font-size: 1.1em;
}
.lp-card .topic {
color: #1a1a2e;
line-height: 1.25;
}
.lp-card.milestone {
background: #fce4ec;
border-color: #a02060;
border-width: 2px;
}
.lp-card.milestone .topic {
font-weight: bold;
}
</style>
<div class="lp-grid">
<div class="lp-card"><div class="head"><span class="num">1</span><span>08.05.</span></div><div class="topic">Intro · Projektgruppen · Internet 101</div></div>
<div class="lp-card"><div class="head"><span class="num">2</span><span>15.05.</span></div><div class="topic">HTML und CSS (Frameworks)</div></div>
<div class="lp-card"><div class="head"><span class="num">3</span><span>22.05.</span></div><div class="topic">JS (Frameworks) und npm</div></div>
<div class="lp-card"><div class="head"><span class="num">4</span><span>29.05.</span></div><div class="topic">nodeJS: Scripting, Running, Building</div></div>
<div class="lp-card"><div class="head"><span class="num">5</span><span>05.06.</span></div><div class="topic">Express API, CRUD, Middlewares</div></div>
<div class="lp-card milestone"><div class="head"><span class="num">6</span><span>12.06.</span></div><div class="topic">Projektwerkstattbericht</div></div>
<div class="lp-card"><div class="head"><span class="num">7</span><span>19.06.</span></div><div class="topic">Testing (Unit · Integration · E2E)</div></div>
<div class="lp-card"><div class="head"><span class="num">8</span><span>26.06.</span></div><div class="topic">TypeScript</div></div>
<div class="lp-card"><div class="head"><span class="num">9</span><span>03.07.</span></div><div class="topic">Docker · Proxies · DBs</div></div>
<div class="lp-card"><div class="head"><span class="num">10</span><span>10.07.</span></div><div class="topic">Wrap-up & Projektsupport</div></div>
<div class="lp-card milestone"><div class="head"><span class="num">11</span><span>17.07.</span></div><div class="topic">Präsentation</div></div>
</div>
---
# Erwartungen und Wünsche
- Was haben wir bisher gelernt?
- Was kennen wir bereits aus dem Arbeitskontext?
- Welche Technologien nutzen wir bereits?
- Was möchte ich hier lernen: Frontend, Backend, DevOps?
- Eigene FOSS entwickeln?
- CLI-Skills verbessern?
- Cloud, Cloud, Cloud?
---
# Prüfungsleistung – Übersicht
Eigenes Projekt: **Web-Applikation** XOR **Backend** (API/CLI).
| Punkte | Bereich |
|--------|---------|
| 30 | Grundanforderungen (Projektgrundlagen + Präsentation) |
| 25 | Clean Code Prinzipien |
| 25 | Accessibility & UX |
| 20 | Technische Umsetzung |
| **100** | **Basispunkte** |
| +15 | Bonuspunkte (max. 3 Features a 5 P.) |
---
# Prüfungsleistung – Grundanforderungen (30 P.)
**Projektgrundlagen (15 P.)**
- Funktionsfähige Anwendung (8 P.)
- Repository mit Versionskontrolle (4 P.)
- Projektdokumentation / README (3 P.)
**Präsentation (15 P.)**
- Elevator Pitch (5 P.)
- Technische Präsentation (5 P.)
- Projektevolution: wie hat sich das Projekt vs. Ursprungsidee verändert? (5 P.)
---
# Prüfungsleistung – Clean Code (25 P.)
**Projektstruktur (10 P.)**
- Ordnerstruktur (5 P.)
- Datei-Organisation (5 P.)
**Code-Qualität (10 P.)**
- KISS – Keep It Simple, Stupid (3 P.)
- Beschreibende Namen (3 P.)
- Funktionsverantwortlichkeiten (4 P.)
**Kommentierung & Dokumentation (5 P.)**
---
# Prüfungsleistung – Accessibility & UX (25 P.)
**Web Accessibility (15 P.)**
- Semantisches HTML (5 P.)
- ARIA Labels & Alt-Texte (5 P.)
- Tastaturnavigation (5 P.)
**Responsive Design (10 P.)**
- Mobile First Approach (5 P.)
- Flexbox/Grid Layout (5 P.)
---
# Prüfungsleistung – Technische Umsetzung (20 P.)
- Plattformunabhängigkeit (5 P.)
- Package Management (5 P.)
- Build Tools & Optimierung (5 P.)
- Live-Demo (5 P.)
---
# Prüfungsleistung – Bonuspunkte (max. 15 P.)
Mindestens 2 Bereiche wählen, je 5 Punkte:
- API Integration
- Local Storage / State Management
- Progressive Web App Features
- Testing
- Erweiterte Responsive Features (3+ Breakpoints)
- Security Features
---
# Prüfungsleistung – Abgabe & Termine
- **Code-Upload:** bis 27.07.
- **Präsentation:** 17.07. (Gruppen, ~10 Min.)
Details: https://git.librete.ch/DHBW/pruefungsleistung
---
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Was ist (k)eine Web Applikation?
## Exkurs
---
# Was ist (k)eine Web Applikation?
**Keine Web Applikation ist:**
* Betriebssystem (GNU/Linux, Windows, macOS, iOS, Android)
* Desktop-Applikation
* Mobile-Applikation
* Embedded System (Microcontroller, IoT)
**Eine Web Applikation ist:**
* plattformunabhängig (läuft im Browser)
* nutzt in der Regel HTTP-Requests
* keine Installation nötig — serverseitig aktualisiert
* gebaut in Web-Technologien (HTML, CSS, JS/TS)
* skalierbar durch verteilte Architektur
---
# Internet-Timeline
| Jahr | Meilenstein |
|------|-------------|
| **1966** | ARPANET-Projekt startet (DARPA) |
| **1969** | Erste Verbindung zwischen UCLA und Stanford |
| **1974** | TCP/IP-Protokoll entwickelt |
| **1989** | Tim Berners-Lee erfindet das WWW (CERN) |
| **1993** | Mosaic — erster grafischer Browser |
| **1994** | W3C gegründet (HTML, CSS, SVG, WCAG) |
| **1995** | ECMAScript / JavaScript wird Standard |
| **2008** | V8 JavaScript Engine (Chrome / Node.js) |
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/arpanet.jpg)
---
# Das WWW (1989)
**Tim Berners-Lee** am **CERN** (Genf)
**Problem:** Wissenschaftler:innen können Forschungsdokumente nicht verlinken — Inkompatible Systeme, kein gemeinsames Format.
**Lösung — drei Bausteine:**
- **HTML** — Auszeichnungssprache für Dokumente
- **HTTP** — Protokoll zur Übertragung
- **URL** — eindeutige Adressen
**1991:** erste Webseite geht online · 1993 vom CERN gemeinfrei freigegeben.
→ WWW ≠ Internet. Das WWW ist eine **Anwendung auf** dem Internet.
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/mosaic.jpg)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg cover](./assets/img_633d7b91366e0-1025x538-768x403.jpg)
---
# Heute: Physische Infrastruktur
- **5,5 Milliarden** Menschen online
- **> 400 Unterseekabel**, ca. **1,3 Mio. km** Glasfaser
- Daten reisen mit **Lichtgeschwindigkeit**
- Latenz Frankfurt — New York: ~80 ms
→ Keine "Cloud" ohne Kabel. Das Internet ist **physische** Infrastruktur — und damit angreifbar.
**Geopolitik:** Five-Eyes-Allianz (USA, UK, CA, AU, NZ) zapft Unterseekabel ab (Programm "Tempora", aufgedeckt 2013 durch Snowden). DE ist Teil der Fourteen Eyes.
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/browser-server-sequence.png)
---
# HTTP – Hypertext Transfer Protocol
**Request:**
```
GET /products HTTP/1.1
Host: localhost:8080
```
**Response:**
```
HTTP/1.1 200 OK
Content-Type: application/json
[{"id": 1, "name": "Produkt A"}, ...]
```
---
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Onboarding
## Tools, Runtimes, Workflows
---
# Werkzeuge & Betriebssystem
**Erforderliche Werkzeuge:**
- **Terminal** (zsh / bash / fish / pwsh) — Kommandozeile
- **Editor** (siehe folgende Folie)
- **JavaScript-Runtime** (Node.js / Bun / Deno)
- **Browser mit DevTools** (Chromium / Firefox / Safari)
- **Git** — verteiltes Versionskontrollsystem (Stand der Industrie)
**OS-Empfehlung:**
| OS | Status |
|----|--------|
| **macOS** | First-class, viele Tools "just work" |
| **Linux** | First-class, beste Server-Parität |
| **Windows + WSL2** | Empfohlen — Windows-CLI hat Edge Cases |
| **Windows nativ** (PowerShell) | Funktioniert, aber holpriger Pfad |
---
# Node.js installieren – macOS
Empfohlen: **LTS** (aktuell **Node.js 24** — Stand 2026).
Paketmanager: **[Homebrew](https://brew.sh)**
```bash
# Homebrew installieren (falls nicht vorhanden)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Node.js LTS installieren
brew install node@24
# Version prüfen
node --version
npm --version
```
Alternative: Installer von [nodejs.org](https://nodejs.org)
---
# Node.js installieren – Linux
Empfohlen: **LTS** (aktuell **Node.js 24**).
**Debian / Ubuntu** ([NodeSource](https://github.com/nodesource/distributions)):
```bash
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
```
**Arch / Manjaro:**
```bash
sudo pacman -S nodejs npm
```
**Fedora / RHEL:**
```bash
sudo dnf module install nodejs:24/common
```
→ Distro-Pakete sind oft **veraltet**. Versionsmanager (folgende Folie) bevorzugen.
---
# Node.js installieren – Windows
Empfohlen: **LTS** (aktuell **Node.js 24**).
**Option 1: winget** (empfohlen, Win 10/11):
```powershell
winget install OpenJS.NodeJS.LTS
```
**Option 2: Chocolatey** ([chocolatey.org](https://chocolatey.org)):
```powershell
choco install nodejs-lts
```
**Option 3: Scoop** ([scoop.sh](https://scoop.sh)):
```powershell
scoop install nodejs-lts
```
**Option 4:** Installer von [nodejs.org](https://nodejs.org)
→ **Empfehlung:** WSL2 + Linux-Workflow nutzen.
---
# Direkt-Install: Wenn es eng wird
**Typisches Szenario** — drei parallele Projekte mit unterschiedlichen Anforderungen:
| Projekt | Stack | Node-Version |
|---------|-------|--------------|
| Praktikums-API | Express (Legacy) | 18.x (LTS Hydrogen) |
| Studienarbeit | Next.js 15 | 22.x (LTS Jod) |
| Aktuelles Lab-Projekt | neueste Features | 24.x (Iron, current LTS) |
System-Install kennt nur **eine** Node-Version global → Konflikt.
---
# Symptome im Alltag
- `npm install` bricht mit `engines`-Warning ab
- native Module (z. B. `node-sass`) bauen nicht
- CI-Pipeline ist grün, lokal rot — oder umgekehrt
- erheblicher Zeitverlust beim Debuggen von Versions-Konflikten
→ Lösung: **Versionsmanager** und Node-Version pro Projekt pinnen.
---
# Node-Versionsmanager
Verschiedene Projekte brauchen oft verschiedene Node-Versionen. **Lösung:** Versionsmanager.
| Tool | Sprache | Geschwindigkeit | Plattform |
|------|---------|-----------------|-----------|
| **nvm** | Bash | langsam (Shell-Funktion) | macOS, Linux |
| **fnm** | Rust | sehr schnell | macOS, Linux, Windows |
| **asdf** | Bash | mittel, multi-language | macOS, Linux |
| **volta** | Rust | schnell, pinning per `package.json` | alle |
```bash
# fnm
curl -fsSL https://fnm.vercel.app/install | bash
fnm install --lts # aktuell Node 24
fnm use lts-latest
# .nvmrc / .node-version automatisch
echo "lts/*" > .nvmrc
fnm use # liest .nvmrc
```
---
# Alternative Runtimes
| Runtime | Erschienen | Engine | USP |
|---------|------------|--------|-----|
| **Node.js** | 2009 | V8 (C++) | Standard, größtes Ökosystem |
| **Deno** | 2020 | V8 (Rust) | Sicherheit, TypeScript nativ, Std-Lib |
| **Bun** | 2022 | JavaScriptCore (Zig) | Geschwindigkeit, all-in-one |
```bash
# Deno
curl -fsSL https://deno.land/install.sh | sh
deno run main.ts
# Bun
curl -fsSL https://bun.sh/install | bash
bun install
bun run dev
```
→ Im Kurs primär **Node.js**. Bun und Deno sind ebenfalls zulässig.
---
# Editor (IDE)
- **VS Code** (empfohlen) — kostenlos, große Plugin-Auswahl
- **JetBrains WebStorm** — sehr mächtig, kostenpflichtig
- kostenlos für Studierende via [JetBrains Education](https://www.jetbrains.com/community/education/#students)
- **Vim / Neovim / Emacs** — für CLI-Profis
- **Zed** — modern, schnell
---
# Browser DevTools
Chromium / Firefox / Safari — `F12` oder `Cmd+Opt+I`
<small>Chromium = Open-Source-Basis von Chrome, Edge, Brave, Opera, Vivaldi etc.</small>
**Wichtigste Panels:**
- **Network** — HTTP-Requests, Responses, Timing
- **Console** — `console.log`, JS-Errors, REPL
- **Elements** — DOM-Inspektor, Live-CSS-Editor
- **Sources** — Debugger, Breakpoints, Source-Maps
- **Performance** — Profiling, Frame-Rate, FPS
- **Application** — LocalStorage, Cookies, Service Workers
---
# HTTP-Clients
- **Bruno** ([usebruno.com](https://usebruno.com)) — open source, git-friendly
- **Hoppscotch** ([hoppscotch.io](https://hoppscotch.io)) — open source, browser-basiert (vormals Postwoman)
- **Insomnia** / **Postman** — etabliert, freemium
- **curl** / **httpie** — CLI (Command-Line Interface, scriptbar)
---
# Git Setup & Workflow
**Einmalige Konfiguration:**
```bash
git config --global user.name "Vorname Nachname"
git config --global user.email "vorname.nachname@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase true
```
**SSH-Key für GitHub/GitLab:**
```bash
ssh-keygen -t ed25519 -C "vorname.nachname@example.com"
cat ~/.ssh/id_ed25519.pub # → in GitHub-Settings einfügen
```
**Tägliche Befehle:**
```bash
git status # was hat sich geändert?
git add <file> # für Commit vormerken
git commit -m "feat: ..." # Conventional Commits
git push # hochladen
git pull --rebase # neueste Änderungen holen
```
---
# Projekt-Skeleton
```bash
mkdir mein-projekt && cd mein-projekt
git init
npm init -y # erzeugt package.json
echo "node_modules" > .gitignore
echo ".env" >> .gitignore
npm install express
```
**`package.json` minimal:**
```json
{
"name": "mein-projekt",
"version": "0.1.0",
"type": "module",
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js"
}
}
```
`.env` für Secrets (nie committen!), `.env.example` mit Dummy-Werten committen.
---
File diff suppressed because it is too large. Load diff
+412
View File
@@ -0,0 +1,412 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: JavaScript & Frameworks
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
![bg cover opacity:0.2](./assets/background.png)
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 3 — JS, Frameworks & Build Tools
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
# Ressourcen zum Selbstlernen
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# JavaScript
---
# ECMAScript
- **Ecma International** — European Computer Manufacturers Association
- Seit **1997** standardisiert als **ECMA-262**
- Implementierungen: **JavaScript** (Netscape, später Mozilla), **JScript** (Microsoft), **ActionScript** (Adobe Flash) etc.
- Aktuelle Version: **ECMAScript 2024 (ES15)**, jährliche Releases
**Browser-Kompatibilität:**
- Faustregel: **ES2017+** läuft in allen modernen Browsern
- Ältere Targets via Transpiler (Babel, esbuild, swc, tsc)
- Kompatibilitätstabelle: [kangax.github.io/compat-table](https://kangax.github.io/compat-table/)
---
# JavaScript Grundlagen
```javascript
// Variablen
let name = "Welt";
const alter = 25;
// Funktionen
function gruss(name) {
return `Hallo, ${name}!`;
}
// Arrow Functions
const add = (a, b) => a + b;
// Arrays
const arr = [1, 2, 3];
arr.map(x => x * 2); // [2, 4, 6]
```
---
# DOM Manipulation
```javascript
// Element auswählen
const title = document.querySelector('h1');
// Inhalt ändern
title.textContent = 'Neue Überschrift';
// Event Listener
button.addEventListener('click', () => {
alert('Geklickt!');
});
```
---
# Fetch API
```javascript
fetch('/api/products')
.then(res => res.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
```
---
# Async/Await
```javascript
async function loadProducts() {
try {
const res = await fetch('/api/products');
const data = await res.json();
return data;
} catch (err) {
console.error('Fehler:', err);
}
}
```
---
<!-- _header: '' -->
<!-- _footer: '' -->
<!-- _backgroundColor: #050517 -->
![bg fit](./assets/demos/web-engineering-mandala.png)
---
<!-- _backgroundColor: #050517 -->
<!-- _color: #e2e8f0 -->
# Web Engineering Mandala
Interaktive version: [nextlevelshit.github.io/web-engineering-mandala](https://nextlevelshit.github.io/web-engineering-mandala/)
3 Schichten:
- **Core Layer** — JS, HTML, CSS (vanilla)
- **Build Layer** — bundlers, runtimes, template engines, pre/post processors
- **Application Layer** — frontend, backend, frameworks, SSR, CSS-utilities
3 Segmente: HTML (blau) · JavaScript (rot) · CSS (grün) · Mittelpunkt: `package.json`
---
# JavaScript Frameworks – Kategorien
| Kategorie | Beispiele |
|-----------|-----------|
| **CSS Frameworks** | Tailwind, Bootstrap, shadcn/ui |
| **Frontend Frameworks** | React, Vue, Svelte, Astro |
| **Rendering / Meta** | Next.js, Nuxt, Gatsby |
| **Build Tools / Bundler** | Webpack, Vite, Parcel, esbuild |
| **Backend Frameworks** | Express, Fastify, NestJS |
---
# Vanilla JS – Counter
```html
<button id="myButton">Clicked 0 times</button>
<script>
let count = 0;
const btn = document.getElementById('myButton');
btn.addEventListener('click', () => {
count++;
btn.textContent = `Clicked ${count} times`;
});
</script>
```
**Pro:** hohe Kompatibilität, simpel
**Contra:** State-Management und Re-Rendering werden schnell komplex
---
# React – Counter (JSX)
![bg right:25% fit](./assets/logos/frameworks/react.svg)
```jsx
import { useState } from "react";
function MyButton() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
```
- **Component-Based** Architecture
- **Virtual DOM**, JSX, Hooks
- großes Ökosystem (React Router, Redux, …)
---
# Vue 3 – Composition API
![bg right:25% fit](./assets/logos/frameworks/vue.svg)
```vue
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<button @click="count++">
Clicked {{ count }} times
</button>
</template>
```
- **Reactive Data Binding**
- Single-File Components (`.vue`)
- Vue Router, Pinia (State)
---
# Svelte – Counter
![bg right:25% fit](./assets/logos/frameworks/svelte.svg)
```svelte
<script>
let count = 0;
</script>
<button on:click={() => count += 1}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
```
- **Compiler-Based** – kein Runtime-Framework
- Reactive Assignments (`let`)
- Built-in Animationen
---
# Astro – Content-First
![bg right:25% fit](./assets/logos/frameworks/astro.svg)
```astro
---
const greeting = "Hallo DHBW";
---
<html>
<body>
<h1>{greeting}</h1>
<MyReactButton client:load />
</body>
</html>
```
- **Islands Architecture** – Komponenten mehrerer Frameworks mischen
- Default: kein JS im Output (nur statisches HTML)
- Hydration on demand (`client:load`, `client:visible`)
---
# Rendering Frameworks
| | Frontend | SSR | SSG | File Routing |
|---|---|---|---|---|
| **Next.js** | React | ja | ja | `/app/page.js` |
| **Nuxt** | Vue | ja | ja | `/pages/*.vue` |
| **Gatsby** | React | nein | ja | GraphQL Data |
---
# Build Tools – Webpack
![bg right:25% fit](./assets/logos/frameworks/webpack.svg)
- Module Bundling (JS, CSS, Images)
- Module Loaders + großes Plugin-Ökosystem
- Code Splitting, HMR
- Konfigurations-lastig (`webpack.config.js`)
→ Standard für viele Legacy-Setups.
---
# Build Tools – Vite
![bg right:25% fit](./assets/logos/frameworks/vite.svg)
```javascript
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://localhost:4567",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, "")
}
}
}
});
```
- Rollup unter der Haube, **HMR mit ES Modules**
- Tree-Shaking, Code Splitting, React + Vue out-of-the-box
---
# Build Tools – Parcel
![bg right:25% fit](./assets/logos/frameworks/parcel.png)
```json
{
"name": "my-project",
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel",
"build": "parcel build"
},
"devDependencies": {
"parcel": "latest"
}
}
```
- **Zero-Config**
- Auto-Resolution, HMR
- TS / CSS-Preprocessors out-of-the-box
---
# Microfrontends
- Mehrere unabhängige Frontend-Apps in einer Page
- Module Federation (Webpack 5)
- **single-spa**: https://single-spa.js.org/
- Anwendungsfall: große Teams, unterschiedliche Tech-Stacks pro Domain
→ Komplexitätskosten: nur bei echtem Skalierungsbedarf.
---
# Demo-Repos
- https://github.com/nextlevelshit/dhbw-client-js (Counter, multi-framework)
- https://github.com/nextlevelshit/node-cache-api (API + Tests)
- https://github.com/nextlevelshit/dhbw-docker (Compose-Stack)
+293
View File
@@ -0,0 +1,293 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 4 — Node.js: Scripting, Running and Building
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
# Ressourcen zum Selbstlernen
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
---
# Inhalt
1. Node.js: was und warum
2. `node --check`, `node -e` (One-Liner)
3. Module: `require` / `import`, `__dirname` / `__filename`
4. HTTP-Server (built-in)
5. `fs` — Dateien lesen / schreiben
6. `package.json` — Aufbau und Lifecycle
7. npm Scripts
8. Module exportieren
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Node.js – Scripting, Running and Building
---
# Node.js – Was ist das?
- JavaScript Runtime (V8 Engine)
- Server-side JavaScript
- npm Package Manager
- Ideal für: APIs, CLI Tools, Automation
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/nodejs-usecases.png)
---
# node --check und node -e
```bash
# Syntax check only
node --check script.js
# One-liner
node -e "console.log('Hallo Welt')"
```
---
# node -e Beispiele
```bash
# String
node -e "console.log('Hello ' + process.argv[2])" Welt
# File reading (ESM via --input-type)
node --input-type=module -e \
"import { readFileSync } from 'node:fs'; \
console.log(readFileSync('data.json', 'utf8'))"
# JSON parsen
node -e "console.log(JSON.parse(process.argv[1]))" '{"a":1}'
```
---
# Module laden – ESM (modern)
`"type": "module"` in `package.json` einschalten.
```javascript
// Eigene Datei (Endung .js erforderlich!)
import { add, greet } from "./utils.js";
// npm Modul (default + named import)
import express from "express";
import { Router } from "express";
// Built-in (mit "node:"-Präfix)
import { readFileSync } from "node:fs";
import { join } from "node:path";
```
<small>**Legacy CJS:** `const express = require("express")` — funktioniert noch, aber ESM ist heute Standard.</small>
---
# Pfad-Auflösung in ESM
```javascript
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, "..", "config.json");
```
<small>In ESM gibt es **kein** automatisches `__dirname` / `__filename` (anders als CJS). Über `import.meta.url` rekonstruieren.</small>
---
# HTTP Server mit Node.js (Built-in)
**ESM** (modern, `"type": "module"` in `package.json`):
```javascript
import { createServer } from "node:http";
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<h1>Hallo Welt!</h1>");
});
server.listen(3000, () => {
console.log("Server läuft auf Port 3000");
});
```
<small>**CommonJS** (legacy): `const http = require("http");` — funktioniert noch, aber neue Projekte nutzen ESM.</small>
---
# fs – Dateien lesen/schreiben
```javascript
import { readFile, readFileSync, writeFile } from "node:fs";
import { readFile as readFileAsync } from "node:fs/promises";
// Promise-API (empfohlen, modern)
const data = await readFileAsync("data.json", "utf8");
console.log(JSON.parse(data));
// Callback-API
readFile("data.json", "utf8", (err, data) => {
if (err) throw err;
console.log(JSON.parse(data));
});
// Sync (nur für Setup / Scripts)
const content = readFileSync("data.json", "utf8");
```
---
# package.json – Das Herzstück
```json
{
"name": "mein-projekt",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"test": "node --test"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"nodemon": "^3.0.0"
}
}
```
---
# npm Scripts
```bash
# npm run <script>
npm run start
npm run dev
# Mit Argumenten
npm run build -- --production
# Lifecycle scripts
npm install → postinstall
npm start → start (direkt)
```
---
# Node.js Module – exports (ESM)
```javascript
// utils.js — named exports
export const add = (a, b) => a + b;
export const greet = (name) => `Hallo ${name}!`;
// utils.js — default export
export default {
add,
greet
};
```
```javascript
// nutzen
import { add, greet } from "./utils.js"; // named
import utils from "./utils.js"; // default
import utils, { add } from "./utils.js"; // beides
```
<small>**Legacy CJS:** `module.exports = { add, greet }` + `const { add } = require("./utils")`.</small>
+305
View File
@@ -0,0 +1,305 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 5 — Express API, CRUD und Middlewares
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
# Ressourcen zum Selbstlernen
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
---
# Inhalt
1. Node.js — Fun Facts & alternative Runtimes
2. `process` — Signal-Handling, Graceful Shutdown
3. Express — Setup, Routing
4. Middlewares (eigene + built-in)
5. CRUD-Endpunkte (Create, Read, Update, Delete)
6. Error Handling
7. WebSockets
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Node.js – Advanced
---
# node.js – Fun Facts
- Entwickelt von Ryan Dahl in 2009
- Erste serverseitige Laufzeitumgebung in JavaScript (C++)
- Inspiriert von Nginx (Non-blocking I/O)
**Alternativen heute:**
- **Deno** (2020): Rust, Tokio
- **Bun** (2022): Zig
---
# process – Prozess-Steuerung
```javascript
process.on("SIGINT", () => {
console.log("Received SIGINT. Closing server...");
server.close(() => {
console.log("Server closed.");
process.exit(0);
});
});
// Signale
process.exit(0); // Erfolgreich
process.exit(1); // Fehler
```
---
# node.js Module System
```javascript
// ES Modules (modern, "type": "module")
import express from "express";
import { port } from "./src/config/constants.js";
import { readFileSync } from "node:fs";
```
```javascript
// CommonJS (legacy)
const express = require("express");
const fs = require("fs");
```
→ Neue Projekte: ESM. Built-ins immer mit `node:`-Präfix.
---
# HTTP Request mit node-fetch
```javascript
// Node.js 18+ hat fetch eingebaut
const res = await fetch('https://api.example.com/data');
const data = await res.json();
// Ältere Versionen: node-fetch
import fetch from 'node-fetch';
```
---
# Web Server, Builder, Bundler
```bash
# Boilerplates erstellen
npm create vite@latest my-app -- --template react
npm create svelte@latest
npm create astro@latest
# nuxt
npm install nuxt
npm run dev
```
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/js-frameworks-overview.png)
---
# Express – Grundlagen
```javascript
import express from 'express';
const app = express();
app.listen(port, () => {
console.log(`Server läuft auf Port ${port}`);
});
```
---
# Express – Middleware
```javascript
app.use(express.json());
app.use('/api', (req, res, next) => {
console.log('Time:', Date.now());
next();
});
app.use((req, res, next) => {
console.log('Middleware 2');
next();
});
```
---
# Express – CRUD Routes
```javascript
app.get('/api/:key', async (req, res) => {
const context = cache.get(req.params.key);
res.json({ context });
});
app.post('/api/:key', async (req, res) => {
const payload = req.body;
res.json({ created: cache.set(req.params.key, payload) });
});
app.put('/api/:key', async (req, res) => {
const payload = req.body;
res.json({ updated: cache.update(req.params.key, payload) });
});
app.delete('/api/:key', async (req, res) => {
cache.remove(req.params.key);
res.json({ deleted: req.params.key });
});
```
---
# Express – server.listen
```javascript
const server = app.listen(port, () => {
console.log(`Server läuft auf Port ${port}`);
});
server.on('error', (err) => {
console.error('Server error:', err);
});
server.close(() => {
console.log('Server geschlossen');
});
```
---
# WebSockets
```javascript
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client verbunden');
ws.on('message', (data) => {
console.log('Empfangen:', data.toString());
ws.send('Pong');
});
});
```
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/ts-js-compilation.png)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/docker-orchestration.png)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/demos/test-automation-trophy.png)
+383
View File
@@ -0,0 +1,383 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: Testing
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 7 — Testing
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
# Ressourcen zum Selbstlernen
- **CODE CRISPIES** — [codecrispi.es](https://codecrispi.es/)
- **Online Code-Editor** — [codepen.io](https://codepen.io/pen/)
- **MDN** (Mozilla Developer Network) — [developer.mozilla.org/de/](https://developer.mozilla.org/de/)
- **Flexbox-Spiel** — [flexboxfroggy.com](https://flexboxfroggy.com/)
- **Grid-Spiel** — [cssgridgarden.com](https://cssgridgarden.com/)
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Testing
## Unit · Integration · End-to-End
---
# Inhalt
1. Test Automation Pyramid
2. Test Automation Trophy
3. Static Code Analysis
4. Unit Tests
5. Integration Tests
6. End-to-End Tests (API + Client)
7. Live Coding
---
# Test Automation Pyramid
```
▲ manual / exploratory
▲ ▲ E2E (UI + API)
▲▲▲▲ Integration (class + real deps)
▲▲▲▲▲▲ Unit (class + mocked deps)
▲▲▲▲▲▲▲▲ Static analysis (compiler, linter)
```
- Oben: wenige, langsam, teuer, brüchig
- Unten: viele, schnell, billig, stabil
---
# Test Automation V-Modell
```
Requirements ────────────► Acceptance Test
▼ ▲
Architecture ────────► Integration Test
▼ ▲
Modular Design ──────► Unit Test
▼ ▲
Implementation
```
Jede Design-Phase hat Testpendant. Testfälle früh ableiten.
---
# Test Automation Trophy (Kent C. Dodds)
```
┌─────────┐
│ E2E │ ← klein
├─────────┤
│ Integr. │ ← GROSS (Hauptfokus)
├─────────┤
│ Unit │ ← mittel
├─────────┤
│ Static │ ← Basis (TS, ESLint)
└─────────┘
```
Andere Gewichtung: **Integration im Zentrum**, Static als breite Basis.
---
# Testing Frameworks Übersicht
**Static Code Analysis**
- ESLint, Prettier, SonarQube, TypeScript
**Unit + Integration**
- jest, mocha, **vitest**
**End-to-End**
- **Cypress**, **Playwright**, supertest (API only)
**Visual / Interaction**
- Storybook, Chromatic
---
# Static Code Analysis
- Läuft in Millisekunden
- Findet Syntaxfehler, ungenutzte Variablen, undefined refs
- Kein Runtime-Overhead
- **Erste Verteidigungslinie**
```bash
npm i -D eslint prettier
npx eslint --init
npx prettier --write .
```
`SonarQube`: zentraler Server, Code Smells, Security, Coverage.
---
# Unit Tests – Eigenschaften
- **Isolation** – jede Dependency gemockt
- **Deterministisch** – gleicher Input → gleicher Output
- **Schnell** – kein I/O, kein Netzwerk, kein FS
- **Fokussiert** – eine Funktion pro Test
---
# Unit Test – Beispiel (vitest)
```javascript
// cache.unit.test.js
import { describe, it, expect } from "vitest";
import { Cache } from "./cache.js";
describe("Cache", () => {
it("creates and retrieves data with custom key", () => {
const cache = new Cache();
const data = { user: "wolfgang", city: "stuttgart" };
const key = cache.create(data, "custom-key");
expect(key).toBe("custom-key");
expect(cache.get("custom-key")).toEqual(data);
});
});
```
---
# Integration Tests – Eigenschaften
- **Reale Komponenten** – echte Cache + Express-Instanzen
- **Supertest-Magie** – simuliert HTTP ohne Server-Startup
- **In-Process** – alles im selben Node-Prozess
- **Kein Netzwerk** – keine TCP-Connections
→ teste Zusammenspiel mehrerer Module ohne Deployment.
---
# Integration Test – Beispiel (supertest)
```javascript
// app.integration.test.js
import request from "supertest";
import { app, cache } from "./app.js";
test("full CRUD lifecycle with real cache", async () => {
const res = await request(app)
.post("/api/entries")
.send({ name: "integration-test", status: "active" });
expect(res.status).toBe(201);
const { key } = res.body;
expect(cache.keys()).toContain(key);
});
```
---
# End-to-End Tests (API)
- **Echter Server** – HTTP auf TCP-Port
- **Echtes Netzwerk** – `fetch` über localhost
- **Echte Umgebung** – CORS, Middleware-Stack
- **User-Perspektive** – exakt was Clients sehen
---
# E2E API Test – Beispiel
```javascript
// index.e2e.test.js
import { createServer } from "node:http";
import { app } from "./app.js";
let server;
const TEST_PORT = 8081;
const baseUrl = `http://localhost:${TEST_PORT}`;
beforeAll(async () => {
server = createServer(app);
await new Promise((r) => server.listen(TEST_PORT, r));
});
afterAll(() => server.close());
test("complete user journey", async () => {
const res = await fetch(`${baseUrl}/api`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: "e2e", timestamp: Date.now() }),
});
expect(res.status).toBe(201);
});
```
---
# End-to-End Tests (Client)
- **Echter Browser** – Chromium / Firefox / WebKit
- **Echtes DOM** – Klicks, Tippen, Scrollen
- **Echtes Rendering** – CSS, async Operationen
- **Cross-Origin** – Client- und Server-Kommunikation
Tools: **Cypress**, **Playwright**
---
# Cypress – Minimalbeispiel
```javascript
// cypress/e2e/counter.cy.js
describe("Counter App", () => {
it("increments on click", () => {
cy.visit("http://localhost:3000");
cy.contains("button", "Clicked 0 times").click();
cy.contains("button", "Clicked 1 times");
});
});
```
```bash
npx cypress open # GUI
npx cypress run # headless / CI
```
---
# Playwright – Minimalbeispiel
```javascript
// tests/counter.spec.js
import { test, expect } from "@playwright/test";
test("counter increments", async ({ page }) => {
await page.goto("http://localhost:3000");
await page.getByRole("button").click();
await expect(page.getByRole("button")).toContainText("Clicked 1 times");
});
```
```bash
npx playwright test
npx playwright test --ui
```
---
# Coverage Reports
```bash
npx vitest run --coverage
```
```
File | % Stmts | % Branch | % Funcs | % Lines
--------------|---------|----------|---------|--------
cache.js | 95.2 | 80.0 | 100.0 | 95.2
app.js | 88.7 | 75.0 | 90.0 | 88.7
```
- `lcov.info` → CI / SonarQube
- HTML Report → `coverage/index.html`
---
# Best Practices
- **AAA**: Arrange · Act · Assert
- Tests sind **Dokumentation** – beschreibend benennen
- Ein Verhalten pro Test
- Keine Logik im Test (keine `if`/`for`)
- **Test Doubles**: stub, mock, spy, fake
- CI: Tests bei jedem Push
---
# Live Coding
Repo: `node-cache-api`
```bash
git clone https://github.com/nextlevelshit/node-cache-api
cd node-cache-api
npm install
npm test # unit + integration
npm run test:e2e # e2e
```
+440
View File
@@ -0,0 +1,440 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: TypeScript
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 8 — TypeScript
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# TypeScript
## JavaScript on Steroids
---
# Inhalt
1. TypeScript: was und warum
2. `tsc` + `ts-node`
3. `tsconfig.json`
4. Type Primitives
5. Union Types, Type Alias, Interface
6. Enum, Generics, Type Casting
7. Compilation: types stripped
8. Decorators + reflect-metadata
---
# TypeScript – Was?
- **Superset von JavaScript** (Microsoft, 2012)
- Jede `.js` lässt sich zu `.ts` umbenennen
- **Type Errors zur Compile-/Transpile-Zeit**
- Neuere ECMAScript-Features verfügbar
- **Im Browser:** läuft nicht direkt – wird zu JS kompiliert
---
# Setup: tsc + ts-node
```bash
npm i -D typescript ts-node @types/node
npx tsc --init # erzeugt tsconfig.json
```
**Run:**
```bash
npx ts-node index.ts # statt: node index.js
```
**Build:**
```bash
npx tsc # kompiliert nach ./dist
node dist/index.js
```
---
# tsconfig.json
```json
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
---
# Type Primitives
```typescript
let loading: boolean = false;
let count: number = 42;
let pi: number = 3.14;
let name: string = "DHBW ist fetzig!";
let empty: null = null;
let missing: undefined = undefined;
function log(msg: string): void {
console.log(msg);
}
```
`any`, `unknown`, `never`, `void` sind weitere TS-Spezialtypen.
---
# Union Types
```typescript
let id: number | string;
id = 101; // ok
id = "101"; // ok
id = true; // TypeError (nicht erlaubt)
```
---
# Type Alias
```typescript
type NumOrString = number | string;
let id: NumOrString;
id = 101;
id = "abc";
type UserId = string;
type Status = "active" | "inactive" | "pending";
```
---
# Interface
```typescript
interface User {
id: number;
name: string;
email?: string; // optional
readonly createdAt: Date; // schreibgeschützt
greet(msg: string): void;
}
const u: User = {
id: 1,
name: "Lisa",
createdAt: new Date(),
greet(msg) { console.log(msg); }
};
```
---
# Interface vs Type
**Interface**
- nur Objektformen
- kann mehrfach deklariert (Merging)
- bei perf-kritischen Checks oft schneller
**Type Alias**
- alles: Primitive, Union, Tuple, Function-Signaturen
- nicht erweiterbar nach Definition
→ Faustregel: **Interfaces für API-Shapes, Types für alles andere.**
---
# Enum
```typescript
enum Direction {
Up = 1,
Down, // 2
Left, // 3
Right // 4
}
function walk(d: Direction): string {
switch (d) {
case Direction.Up: return "bow";
case Direction.Down: return "stern";
case Direction.Left: return "windward";
case Direction.Right: return "leeward";
}
}
walk(Direction.Up); // "bow"
```
---
# Generics
```typescript
class GenericContainer<T> {
private readonly value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
const numbers = new GenericContainer<number>(10);
const strings = new GenericContainer<string>("Hello");
console.log(numbers.getValue()); // 10
console.log(strings.getValue()); // "Hello"
```
---
# Type Casting
```typescript
let someValue: any = "this is a string";
let len: number = (someValue as string).length;
console.log(len); // 16
// Alt-Syntax (nicht in TSX):
let len2: number = (<string>someValue).length;
```
`as unknown as T` für "double cast" – nur Notbehelf!
---
# Compilation: Types werden gestripped
**TypeScript:**
```typescript
type Result = "pass" | "fail";
function verify(result: Result) {
if (result === "pass") console.log("Passed");
else console.log("Failed");
}
```
**JavaScript (nach `tsc`):**
```javascript
function verify(result) {
if (result === "pass") console.log("Passed");
else console.log("Failed");
}
```
**Types existieren nur zur Compile-Zeit.** Kein Runtime-Check.
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Decorators
## Meta-Programmierung mit TypeScript
---
# TypeScript: Decorators
Vier Typen:
- **Class Decorators** – auf ganze Klasse
- **Property Decorators** – auf Felder
- **Method Decorators** – auf Methoden
- **Parameter Decorators** – auf Methoden-Parameter
Genutzt von: Angular, NestJS, TypeORM, class-validator.
---
# Decorators aktivieren
**`tsconfig.json`:**
```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
```
**Root-File:**
```typescript
import "reflect-metadata";
```
---
# Decorator-Signaturen
```typescript
type ClassDecorator = <T extends Function>(target: T) => T | void;
type PropertyDecorator = (
target: Object,
propertyKey: string | symbol
) => void;
type MethodDecorator = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
) => TypedPropertyDescriptor<T> | void;
type ParameterDecorator = (
target: Object,
propertyKey: string | symbol | undefined,
parameterIndex: number
) => void;
```
---
# Decorator – Beispiel
```typescript
function Log(target: any, key: string, desc: PropertyDescriptor) {
const original = desc.value;
desc.value = function (...args: any[]) {
console.log(`→ ${key}(${JSON.stringify(args)})`);
return original.apply(this, args);
};
}
class Calculator {
@Log
add(a: number, b: number) {
return a + b;
}
}
new Calculator().add(2, 3); // logs: → add([2,3])
```
---
# Reflect: Meta-Daten lesen
```typescript
import "reflect-metadata";
Reflect.defineMetadata("role", "admin", target);
Reflect.hasMetadata("role", target); // true
Reflect.getMetadata("role", target); // "admin"
```
Frameworks (NestJS, TypeORM) nutzen das, um zur **Runtime** Type-Informationen zu rekonstruieren – z. B. für DI, ORM-Mapping, Validierung.
---
# Anwendung: NestJS-Beispiel
```typescript
@Controller("users")
export class UsersController {
constructor(private users: UsersService) {}
@Get(":id")
findOne(@Param("id") id: string) {
return this.users.find(+id);
}
}
```
`@Controller`, `@Get`, `@Param` sind Decorators – Metadaten ⇒ Routing.
+356
View File
@@ -0,0 +1,356 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: Docker und Service Orchestration
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 9 — Docker, Proxies and DBs
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Docker
## Service Orchestration
---
# Inhalt
1. Architektur: Reverse Proxy + API + DB
2. Container vs VM
3. `Dockerfile`
4. `compose.yml`
5. Docker CLI / Compose Befehle
6. Live Demo: `dhbw-docker`
---
# Architektur
```
Browser (localhost:10007)
│
▼
┌──────────────────┐
│ Reverse Proxy │ (nginx / traefik)
└─────────┬────────┘
┌────┴────┐
▼ ▼
┌─────────┐ ┌────────┐
│ Web │ │ API │
│ App │ │ (Node) │
└────┬────┘ └────┬───┘
└─────┬──────┘
▼
┌────────┐
│ DB │ (Postgres)
└────────┘
```
---
# Container vs VM
| | VM | Container |
|---|----|-----------|
| Kernel | eigener | Host-Kernel |
| Boot | Minuten | Sekunden |
| Größe | GB | MB |
| Isolation | stark | namespaces, cgroups |
| Image | komplett | Layer (diff) |
→ **Docker** = Container-Engine + Image-Format + Registry.
---
# Dockerfile – Express API
```dockerfile
FROM node:lts-slim AS builder
WORKDIR /app
COPY ./package*.json ./
RUN npm ci --no-audit --no-fund --no-progress --loglevel=error
COPY ./ ./
ENV NODE_ENV="production"
EXPOSE 8080
CMD ["npm", "start"]
```
**`npm ci`** statt `npm i` für reproduzierbare Builds.
---
# Multi-Stage Build
```dockerfile
FROM node:lts-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:lts-slim AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 8080
CMD ["node", "dist/index.js"]
```
→ kleines Final-Image, keine Build-Tools im Runtime.
---
# .dockerignore
```
node_modules
dist
.git
.env
*.log
coverage
```
Spart Build-Kontext und verhindert Geheimnis-Leaks.
---
# compose.yml – Services
```yaml
name: dhbw-docker-app
services:
api:
build: ./api
container_name: dhbw-api
environment:
DATABASE_USERNAME: ${DATABASE_USERNAME}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
DATABASE_NAME: ${DATABASE_NAME}
DATABASE_HOST: db
DATABASE_PORT: 5432
ports:
- "10000:8080"
networks: [net, data]
depends_on:
db:
condition: service_healthy
```
---
# compose.yml – Datenbank + Proxy
```yaml
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DATABASE_USERNAME}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: ${DATABASE_NAME}
volumes:
- dbdata:/var/lib/postgresql/data
networks: [data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$DATABASE_USERNAME"]
interval: 5s
retries: 5
proxy:
image: nginx:alpine
ports: ["10007:80"]
volumes: ["./nginx.conf:/etc/nginx/nginx.conf:ro"]
networks: [net]
depends_on: [api]
volumes:
dbdata:
networks:
net:
data:
```
---
# Docker Compose – Befehle
```bash
docker compose -p "dhbw-docker-app" \
-f compose.yml \
--env-file .env \
up \
--build \
--remove-orphans \
--force-recreate
```
| Flag | Bedeutung |
|------|-----------|
| `-p` | Projektname |
| `-f` | mehrere Compose-Files möglich |
| `--env-file` | `.env` einlesen |
| `--build` | Images neu bauen |
| `--remove-orphans` | verwaiste Container weg |
| `--force-recreate` | Container neu starten |
---
# Compose – Alltag
```bash
docker compose up -d # detached starten
docker compose ps # Status
docker compose logs -f api # Logs streamen
docker compose exec api sh # Shell im Container
docker compose down -v # Stop + Volumes löschen
docker compose restart api # Neustart einzelner Service
```
---
# Health Checks
```yaml
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
```
`depends_on: { condition: service_healthy }` wartet, bis Health passt.
---
# Networks: Segmentierung
- **`net`** – öffentlich erreichbar (Proxy — App)
- **`data`** – nur intern (App — DB)
- DB ist **nicht** vom Internet erreichbar
- Service-DNS: `api`, `db`, `proxy` (Container-Name = Hostname)
→ Defense in Depth.
---
# Volumes – Daten persistent
```yaml
volumes:
dbdata: # benannt
- ./code:/app # bind mount (dev)
- /app/node_modules # anonymous (überschreibt bind)
```
`docker compose down` ohne `-v` lässt Volumes leben.
---
# Live Demo
Repo: https://github.com/nextlevelshit/dhbw-docker
```bash
git clone https://github.com/nextlevelshit/dhbw-docker
cd dhbw-docker
cp .env.example .env
docker compose up --build
```
→ http://localhost:10007
---
# 100 Concepts of Docker (Exkurs)
- Image · Container · Volume · Network
- Dockerfile · Layer Cache · BuildKit
- Compose · Stack · Swarm · Kubernetes
- Registry · Tag · Digest
- Dev/Prod-Parity (12-Factor)
- Secrets · Configs
- Logging Drivers
- Health Checks · Restart Policies
+370
View File
@@ -0,0 +1,370 @@
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: Best Practices
---
<style>
:root {
--color-foreground: #1a1a2e;
--color-highlight: #d63384;
--color-dimmed: #4a4a6a;
}
section.invert {
--color-foreground: #fff;
}
section {
font-size: 1.4rem;
}
h1 {
color: #a02060;
}
section.invert h1 {
color: #fff;
}
h2 {
color: #1f2937;
}
section.invert h2 {
color: #f48fb1;
}
pre {
background: #0f0f23;
color: #f48fb1;
border-radius: 8px;
border-left: 3px solid #d63384;
}
pre code {
background: transparent;
color: inherit;
}
code {
background: #1a1a2e;
color: #f48fb1;
padding: 0.15em 0.4em;
border-radius: 4px;
}
a {
color: var(--color-highlight);
}
</style>
<!-- _class: invert -->
<!-- _header: '' -->
<!-- _backgroundColor: #000 -->
# Web Engineering
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
**Sommersemester 2026** · Sitzung 10 — Wrap-up & Projektsupport
[https://librete.ch/dhbw/](https://librete.ch/dhbw/)
---
<!-- _header: '' -->
<!-- _footer: '' -->
![bg fit](./assets/qr/slides-dhbw.png)
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Best Practices
## Wrap-up & Projektsupport
---
# Inhalt
1. Semantic Versioning
2. Git Commit Messages (Udacity Style)
3. 12 Factor App
4. Make it Work · Make it Right · Make it Fast
5. Präsentations-Tipps
6. Feedback (Sandwich-Methode)
7. Prüfungsleistung Recap
---
# Semantic Versioning (SemVer 2.0.0)
```
MAJOR.MINOR.PATCH
│ │ └─ Bugfix, abwärtskompatibel
│ └────── Neue Funktion, abwärtskompatibel
└──────────── Breaking Change
```
**Beispiele:**
- `1.4.2` → `1.4.3` Bugfix
- `1.4.3` → `1.5.0` neues Feature
- `1.5.0` → `2.0.0` Breaking Change
Pre-Release: `1.0.0-alpha.1`, `1.0.0-rc.2`
→ https://semver.org/
---
# package.json: Versionsranges
```json
{
"dependencies": {
"express": "^4.19.2", // ≥4.19.2 < 5.0.0 (gleiche MAJOR)
"lodash": "~4.17.21", // ≥4.17.21 < 4.18.0 (gleiche MINOR)
"react": "19.0.0" // exakt
}
}
```
`package-lock.json` pinnt die **tatsächlich installierten** Versionen.
---
# Git Commit Messages – Udacity Style
```
<type>: <Subject>
<blank>
<Body>
<blank>
<Footer>
```
**Type:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
**Subject:** Imperativ, ≤50 Zeichen, klein, kein Punkt
**Body:** **Was** und **Warum** – nicht **Wie**
**Footer:** Issue-Refs, Breaking Changes
---
# Commit-Beispiele
**Gut:**
```
feat: add user logout endpoint
Allows authenticated clients to invalidate their session token
on the server side instead of only deleting the cookie.
Closes #142
```
**Schlecht:**
```
fixed stuff
WIP
update
asdf
```
→ https://udacity.github.io/git-styleguide/
---
# Conventional Commits
```
feat(auth): add OAuth2 login
fix(api): return 404 instead of 500 on missing user
docs: update README install steps
chore(deps): bump express from 4.18 to 4.19
BREAKING CHANGE: drop Node 16 support
```
Vorteile: automatische Changelogs, automatische Version-Bumps (`semantic-release`).
---
# 12 Factor App
Methodik für moderne, skalierbare SaaS-Apps – https://12factor.net/
| # | Faktor |
|---|--------|
| 1 | Codebase – ein Repo, viele Deploys |
| 2 | Dependencies – explizit deklariert (`package.json`) |
| 3 | Config – via Environment-Variablen |
| 4 | Backing Services – als Attached Resources |
| 5 | Build, Release, Run – getrennt |
| 6 | Processes – stateless |
---
# 12 Factor (2/2)
| # | Faktor |
|---|--------|
| 7 | Port Binding – self-contained, exportiert HTTP |
| 8 | Concurrency – horizontal skalieren via Prozesse |
| 9 | Disposability – schneller Start, Graceful Shutdown |
| 10 | Dev/Prod Parity – möglichst gleiche Umgebung |
| 11 | Logs – als Event-Streams (`stdout`) |
| 12 | Admin Processes – als One-Off |
---
# Config via ENV
```javascript
// schlecht
const db = "postgres://user:secret@prod-db:5432/app";
// gut
const db = process.env.DATABASE_URL;
```
```bash
# .env (NICHT committen!)
DATABASE_URL=postgres://user:secret@db:5432/app
NODE_ENV=production
PORT=8080
```
`.env.example` mit Dummy-Werten committen.
---
# Make It Work · Right · Fast
```
1. Make It Work → MVP, hässlich aber funktional
2. Make It Right → Refactor, Tests, Clean Code
3. Make It Fast → Profiling, Optimierung
```
Don't optimize what doesn't work.
Don't refactor what isn't working yet.
→ https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast
---
# KISS · DRY · YAGNI · SOLID
- **KISS** – Keep It Simple, Stupid
- **DRY** – Don't Repeat Yourself
- **YAGNI** – You Aren't Gonna Need It
- **SOLID**:
- **S**ingle Responsibility
- **O**pen/Closed
- **L**iskov Substitution
- **I**nterface Segregation
- **D**ependency Inversion
→ Faustregeln, keine Dogmen.
---
# Präsentation – Aufbau
- **Einstieg** mit aktuellem Bezug (optional)
- **Problemdefinition**: welches Bedürfnis / welches Problem?
- Was hat **blockiert**, wie gelöst?
- **Geschichte mit Spannungsbogen**
- Vorher klären: Fragen während oder nach der Präsentation?
---
# Präsentation – Pitfalls
- Am Thema vorbeireden
- Folien vorlesen
- Live-Demo ohne Backup-Video
- "Sieht man hier nicht so gut, aber..."
- Zu viel Text pro Folie
- Keine klare Botschaft pro Slide
→ **1 Folie = 1 Aussage.**
---
# Feedback – Sandwich-Methode
1. **Eröffnung** — Lob / positive Beobachtungen
2. **Kern** — Kritik + konkreter Verbesserungsvorschlag
3. **Abschluss** — Lob / wertschätzender Ausklang
**Pitfalls:**
- Am Thema vorbeireden
- Bei Unklarheiten keine Fragen stellen
- Subjektives Feedback ("Farbe gefällt mir nicht")
→ **Konkret, sachlich, umsetzbar.**
---
# Prüfungsleistung – Recap
**Grundanforderungen (75 Punkte):**
| Punkte | Kriterium |
|--------|-----------|
| 20 | Idee, Konzeption, Planung |
| 5 | Plattformunabhängigkeit |
| 25 | Clean Code (KISS, SOLID, DI, Testing, Error Handling) |
| 15 | Präsentation |
| 10 | Dokumentation (README, etc.) |
**Zusatzpunkte (max. 10):** TypeScript, Docker, Dev/Prod-Parity, `.env`, npm-Publish, Domain, HTTPS, Responsive Design.
→ https://librete.ch/DHBW/pruefungsleistung
---
# Abgabe + Termine
- **Code-Upload:** bis 27.07.
- **Präsentation:** 17.07. (in Gruppen, ~10 Min)
- Pitch (~1 Min)
- Agenda
- Idee / Problem
- Learnings & Pitfalls
- "It is what it is" – Doku
---
# 70 Things I Regret as a Developer (Auswahl)
- Nicht früher Tests geschrieben zu haben
- Zu viel Zeit mit "perfektem" Setup statt Code
- Nicht öfter `git commit`
- Nicht früher in Pull Requests reviewt
- Keine Doku gepflegt
- Eigene Lösungen für Standard-Probleme
- Frameworks gehyped, Grundlagen vernachlässigt
- "Funktioniert bei mir" akzeptiert
→ Lehre: **Disziplin > Talent.**
---
# Weiterlesen
- https://12factor.net/
- https://semver.org/
- https://udacity.github.io/git-styleguide/
- https://www.conventionalcommits.org/
- https://refactoring.guru/
- https://martinfowler.com/
- MDN Web Docs · web.dev · web.dev/learn
---
<!-- _class: invert -->
<!-- _backgroundColor: #000 -->
# Viel Erfolg mit den Projekten!
Fragen? Probleme? → Telegram, Mail, Sprechstunde.
View File
Whitespace-only changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Browser–Server HTTP Sequenz</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--border: #cbd5e1;
--bg-soft: #f8fafc;
--hl: #005f8a;
--req: #2563eb;
--res: #16a34a;
}
html, body { margin: 0; padding: 0; background: #fff; min-height: 100vh; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 24px 32px; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; }
h1 { margin: 0 0 4px; font-size: 1.45rem; text-align: center; }
.sub { color: var(--muted); margin-bottom: 24px; font-size: 0.88rem; text-align: center; }
.diagram { display: grid; grid-template-columns: 140px 1fr 140px; gap: 0; align-items: start; }
/* Actors */
.actor {
display: flex; flex-direction: column; align-items: center; gap: 6px;
font-weight: 700; font-size: 0.9rem;
}
.actor-box {
border: 2px solid var(--dark); border-radius: 8px;
padding: 8px 18px; background: var(--bg-soft);
white-space: nowrap;
}
.lifeline {
width: 2px; background: var(--border);
height: 100%; min-height: 280px;
margin: 0 auto;
}
/* Messages */
.messages { display: flex; flex-direction: column; gap: 0; padding-top: 48px; }
.msg { display: flex; flex-direction: column; margin-bottom: 6px; }
.msg-arrow {
display: flex; align-items: center; gap: 0; position: relative;
}
.msg-line {
flex: 1; height: 2px;
}
.msg-line.req { background: var(--req); }
.msg-line.res { background: var(--res); background: repeating-linear-gradient(90deg, var(--res) 0 8px, transparent 8px 13px); }
.arrowhead-r { width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; border-left: 10px solid var(--req); }
.arrowhead-l { width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; border-right: 10px solid var(--res); }
.msg-label {
font-size: 0.75rem; font-family: ui-monospace, monospace;
padding: 2px 0 6px; text-align: center;
}
.msg-label.req { color: var(--req); }
.msg-label.res { color: var(--res); }
.gap { height: 10px; }
.legend {
margin-top: 20px; display: flex; gap: 24px; justify-content: center;
font-size: 0.82rem;
}
.leg { display: flex; align-items: center; gap: 8px; }
.leg-line { width: 28px; height: 2px; }
.leg-line.req { background: var(--req); }
.leg-line.res { background: repeating-linear-gradient(90deg, var(--res) 0 6px, transparent 6px 10px); }
</style>
</head>
<body>
<h1>Browser–Server: HTTP Kommunikation</h1>
<p class="sub">Jede Ressource = eigener Request — Browser blockiert nicht, lädt parallel</p>
<div class="diagram">
<!-- Browser actor -->
<div class="actor">
<div class="actor-box">🌐 Browser</div>
<div class="lifeline"></div>
</div>
<!-- Messages -->
<div class="messages">
<div class="msg">
<div class="msg-arrow"><div class="msg-line req"></div><div class="arrowhead-r"></div></div>
<div class="msg-label req">GET /products</div>
</div>
<div class="msg">
<div class="msg-arrow"><div class="arrowhead-l"></div><div class="msg-line res"></div></div>
<div class="msg-label res">HTTP 200 + index.html</div>
</div>
<div class="gap"></div>
<div class="msg">
<div class="msg-arrow"><div class="msg-line req"></div><div class="arrowhead-r"></div></div>
<div class="msg-label req">GET /style.css</div>
</div>
<div class="msg">
<div class="msg-arrow"><div class="arrowhead-l"></div><div class="msg-line res"></div></div>
<div class="msg-label res">HTTP 200 + style.css</div>
</div>
<div class="gap"></div>
<div class="msg">
<div class="msg-arrow"><div class="msg-line req"></div><div class="arrowhead-r"></div></div>
<div class="msg-label req">GET /script.js</div>
</div>
<div class="msg">
<div class="msg-arrow"><div class="arrowhead-l"></div><div class="msg-line res"></div></div>
<div class="msg-label res">HTTP 200 + script.js</div>
</div>
<div class="gap"></div>
<div class="msg">
<div class="msg-arrow"><div class="msg-line req"></div><div class="arrowhead-r"></div></div>
<div class="msg-label req">GET /api/products</div>
</div>
<div class="msg">
<div class="msg-arrow"><div class="arrowhead-l"></div><div class="msg-line res"></div></div>
<div class="msg-label res">HTTP 200 + JSON</div>
</div>
</div>
<!-- Server actor -->
<div class="actor">
<div class="actor-box">🖥️ Server</div>
<div class="lifeline"></div>
</div>
</div>
<div class="legend">
<div class="leg"><div class="leg-line req"></div><span style="color:var(--req)">Request (Browser → Server)</span></div>
<div class="leg"><div class="leg-line res"></div><span style="color:var(--res)">Response (Server → Browser)</span></div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Docker: Service Orchestration</title>
<style>
:root {
--dark: #1a1a2e;
--muted: #6b7280;
--border: #cbd5e1;
--bg-soft: #f8fafc;
--blue: #2563eb;
--red: #dc2626;
}
html, body { margin: 0; padding: 0; background: #fff; min-height: 100vh; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
body { padding: 28px 36px; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; }
h1 { margin: 0 0 4px; font-size: 1.45rem; }
.sub { color: var(--muted); margin-bottom: 28px; font-size: 0.88rem; }
.diagram { display: flex; align-items: center; gap: 0; }
.node {
display: flex; flex-direction: column; align-items: center; justify-content: center;
border: 2px solid var(--dark); border-radius: 8px;
padding: 12px 16px; background: var(--bg-soft);
font-weight: 700; font-size: 0.9rem; text-align: center; min-width: 90px;
}
.node .sub-label { font-size: 0.72rem; font-weight: 400; color: var(--muted); margin-top: 3px; }
.client { border-radius: 50%; width: 72px; height: 72px; padding: 0; border-color: var(--red); }
.proxy { background: #eff6ff; border-color: var(--blue); min-width: 100px; }
.docker-box {
border: 2px dashed var(--blue); border-radius: 12px;
padding: 16px 20px; display: flex; align-items: center; gap: 0;
background: #f0f7ff;
position: relative;
}
.docker-label {
position: absolute; top: -11px; left: 14px;
background: #fff; padding: 0 6px;
font-size: 0.72rem; font-weight: 700; color: var(--blue);
text-transform: uppercase; letter-spacing: 0.06em;
}
.services { display: flex; flex-direction: column; gap: 12px; }
.db-col { display: flex; flex-direction: column; align-items: center; justify-content: center; }
/* arrows */
.arr { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 0 6px; }
.arr-h { display: flex; flex-direction: column; gap: 3px; align-items: center; }
.line { height: 2px; width: 44px; }
.line.blue { background: var(--blue); }
.line.red { background: var(--red); }
.line.dashed-blue { background: repeating-linear-gradient(90deg, var(--blue) 0 6px, transparent 6px 10px); }
.line.dashed-red { background: repeating-linear-gradient(90deg, var(--red) 0 6px, transparent 6px 10px); }
.arrowhead-r::after { content: '▶'; font-size: 0.65rem; color: inherit; }
.arrowhead-l::before { content: '◀'; font-size: 0.65rem; color: inherit; }
.arr-label { font-size: 0.65rem; color: var(--muted); white-space: nowrap; }
.arr-v { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 2px 0; }
.line-v { width: 2px; height: 28px; }
.line-v.blue { background: var(--blue); }
.line-v.dashed-blue { background: repeating-linear-gradient(180deg, var(--blue) 0 5px, transparent 5px 9px); }
.arr-v .arrowhead-d::after { content: '▼'; font-size: 0.65rem; color: var(--blue); }
.gap-col { display: flex; flex-direction: column; gap: 12px; align-items: center; justify-content: center; }
</style>
</head>
<body>
<h1>Docker: Service Orchestration</h1>
<p class="sub">Client kommuniziert nur mit dem Reverse Proxy — interne Services bleiben isoliert</p>
<div class="diagram">
<!-- Client -->
<div class="node client" style="color:var(--red); border-color:var(--red);">
Client
<div class="sub-label">Browser /<br>Web App</div>
</div>
<!-- arrows client → proxy -->
<div class="arr">
<div class="arr-h" style="color:var(--red)">
<div style="display:flex;align-items:center;gap:2px">
<div class="line red" style="width:36px"></div>
<span class="arrowhead-r" style="color:var(--red)"></span>
</div>
<div class="arr-label">localhost:10007</div>
</div>
<div style="height:8px"></div>
<div class="arr-h" style="color:var(--red)">
<div style="display:flex;align-items:center;gap:2px">
<span class="arrowhead-l" style="color:var(--red)"></span>
<div class="line dashed-red" style="width:36px"></div>
</div>
<div class="arr-label">response</div>
</div>
</div>
<!-- Docker compose boundary -->
<div class="docker-box">
<div class="docker-label">docker compose</div>
<!-- Reverse Proxy -->
<div class="node proxy">
Reverse<br>Proxy
<div class="sub-label">:10007</div>
</div>
<!-- arrows proxy → services -->
<div class="gap-col" style="padding: 0 8px;">
<div class="arr-h" style="color:var(--blue)">
<div style="display:flex;align-items:center;gap:2px">
<div class="line blue" style="width:28px"></div>
<span class="arrowhead-r" style="color:var(--blue)"></span>
</div>
<div class="arr-label">/api/*</div>
</div>
<div style="height:8px"></div>
<div class="arr-h" style="color:var(--blue)">
<div style="display:flex;align-items:center;gap:2px">
<div class="line blue" style="width:28px"></div>
<span class="arrowhead-r" style="color:var(--blue)"></span>
</div>
<div class="arr-label">/*</div>
</div>
</div>
<!-- Services column -->
<div class="services">
<div class="node" style="background:#dbeafe; border-color:var(--blue);">
Express API
<div class="sub-label">:3000</div>
</div>
<div class="node" style="background:#dcfce7; border-color:#16a34a;">
Web App
<div class="sub-label">:5173</div>
</div>
</div>
<!-- arrows → DB -->
<div class="gap-col" style="padding: 0 6px;">
<div class="arr-h" style="color:var(--blue)">
<div style="display:flex;align-items:center;gap:2px">
<div class="line dashed-blue" style="width:24px"></div>
<span class="arrowhead-r" style="color:var(--blue)"></span>
</div>
<div class="arr-label">:5432</div>
</div>
<div style="height:8px"></div>
<!-- web app does not connect to DB -->
<div style="width:48px"></div>
</div>
<!-- DB -->
<div class="db-col">
<div class="node" style="background:#fef9c3; border-color:#ca8a04; min-width:70px;">
DB
<div class="sub-label">PostgreSQL</div>
</div>
</div>
</div><!-- /docker-box -->
</div><!-- /diagram -->
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Loaded 100 of 159 files, more files were not shown because too many files have changed in this diff. Show more