dhbw: neuer kurs technik I — 4 kapitel + demo assets
418
slides/dhbw/01_web_eng.md
Normal file
@@ -0,0 +1,418 @@
|
||||
---
|
||||
marp: true
|
||||
theme: gaia
|
||||
paginate: true
|
||||
backgroundColor: #fff
|
||||
header: "Web Engineering – DHBW Stuttgart"
|
||||
footer: "Michael Czechowski – SoSe 2025"
|
||||
title: Web Engineering
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-foreground: #1a1a2e;
|
||||
--color-highlight: #005f8a;
|
||||
--color-dimmed: #4a6a7a;
|
||||
}
|
||||
section.invert {
|
||||
--color-foreground: #fff;
|
||||
}
|
||||
section {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
h1 {
|
||||
color: #005f8a;
|
||||
}
|
||||
section.invert h1 {
|
||||
color: #fff;
|
||||
}
|
||||
pre {
|
||||
background: #0f1e2e;
|
||||
color: #00b4d8;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #005f8a;
|
||||
}
|
||||
pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
code {
|
||||
background: #1a3a4a;
|
||||
color: #00b4d8;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
a {
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||

|
||||
|
||||
# Web Engineering
|
||||
|
||||
**DHBW Stuttgart** · Informatik / Wirtschaftsinformatik
|
||||
|
||||
**Sommersemester 2025**
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: lead -->
|
||||
|
||||
# Willkommen!
|
||||
|
||||
## 1. Sitzung: 09.05.2025
|
||||
|
||||
---
|
||||
|
||||
# Wer bist du, was machst du?
|
||||
|
||||
- B.Sc. Wirtschaftsinformatik (Leibniz-FH Hannover)
|
||||
- Angestellt bei PONS Langenscheidt GmbH in Stuttgart
|
||||
- Honorardozent und Berater
|
||||
|
||||
**Bisherige Vorlesungen (DHBW/Leibniz-FH):**
|
||||
- Social Engineering, Mobile Medien, Web Eng
|
||||
|
||||
---
|
||||
|
||||
# Vorlesungsplan
|
||||
|
||||
| Sitzung | Datum | Thema |
|
||||
|---------|-------|-------|
|
||||
| 1 | 09.05. | Formalia, Kennenlernen, Internet 101 |
|
||||
| 2 | 16.05. | HTML und CSS (Frameworks) |
|
||||
| 3 | 23.05. | JS (Frameworks) und npm |
|
||||
| 4 | 30.05. | nodeJS: Scripting, Running and Building |
|
||||
| 5 | 06.06. | Express API, CRUD und "Middlewares" |
|
||||
| 6 | 13.06. | Testing (unit, integration, end-to-end) |
|
||||
| 7 | 20.06. | TypeScript |
|
||||
| 8 | 27.06. | Docker, Proxies and DBs |
|
||||
| 9 | 04.07. | Präsentationen |
|
||||
|
||||
---
|
||||
|
||||
# Prüfungsleistung
|
||||
|
||||
**LN** – Lernnachweis (50%)
|
||||
- Projekte / Hausaufgaben
|
||||
- Präsentation
|
||||
|
||||
---
|
||||
|
||||
# Internet 101 – Zeitleiste
|
||||
|
||||
| Jahr | Ereignis |
|
||||
|------|----------|
|
||||
| 1966 | ARPANET |
|
||||
| 1969 | RFCs |
|
||||
| 1986 | IETF |
|
||||
| 1992 | Internet Society |
|
||||
| 1974 | TCP/IP und HTTP(S) |
|
||||
| 1987 | Domain Names und DNS |
|
||||
| 1993 | "Erster" Browser: Mosaic |
|
||||
| 1994 | W3C (HTML, XML, CSS, SVG, WCAG etc.) |
|
||||
| 1995 | ECMAScript (JS) |
|
||||
| 2006/08 | V8 JS Runtime Engine |
|
||||
| Heute | 1,3 Mio. km Unterseekabel |
|
||||
|
||||
---
|
||||
|
||||
# Internet 101 – Browser Request
|
||||
|
||||
```
|
||||
Browser Server
|
||||
| |
|
||||
|-- GET /products -------------->|
|
||||
|<-- HTTP 200 + index.html -----|
|
||||
| |
|
||||
|-- GET /script.js ------------->|
|
||||
|<-- HTTP 200 + script.js ------|
|
||||
| |
|
||||
|-- GET /api/products ---------->|
|
||||
|<-- HTTP 200 + JSON ------------|
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# 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"}, ...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Entwicklungsumgebung
|
||||
|
||||
**Tools:**
|
||||
- Node.js + npm
|
||||
- VS Code
|
||||
- Git
|
||||
- Chrome DevTools
|
||||
|
||||
---
|
||||
|
||||
# Versionierung mit Git
|
||||
|
||||
```bash
|
||||
git init
|
||||
git add .
|
||||
git commit -m "initial commit"
|
||||
git remote add origin https://github.com/user/repo.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# HTML – Hypertext Markup Language
|
||||
|
||||
---
|
||||
|
||||
# HTML Grundlagen
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Meine Seite</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Überschrift</h1>
|
||||
<p>Textabsatz</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Semantisches HTML
|
||||
|
||||
```html
|
||||
<header>Navigationsbereich</header>
|
||||
<nav>Navigation</nav>
|
||||
<main>
|
||||
<article>
|
||||
<section>
|
||||
<h2>Überschrift</h2>
|
||||
<p>Inhalt</p>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
<footer>Fußzeile</footer>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# HTML – Tabellen und Listen
|
||||
|
||||
```html
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Preis</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Produkt A</td><td>29,99 €</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul>
|
||||
<li>Punkt 1</li>
|
||||
<li>Punkt 2</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# CSS – Cascading Style Sheets
|
||||
|
||||
---
|
||||
|
||||
# CSS Grundlagen
|
||||
|
||||
```css
|
||||
/* Element */
|
||||
body { font-family: Arial, sans-serif; }
|
||||
|
||||
/* Klasse */
|
||||
.button { background: #005f8a; color: white; }
|
||||
|
||||
/* ID */
|
||||
#header { height: 60px; }
|
||||
|
||||
/* Attribut */
|
||||
input[type="text"] { border: 1px solid #ccc; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Layout – Flexbox
|
||||
|
||||
```css
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Layout – Grid
|
||||
|
||||
```css
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS – Box Model
|
||||
|
||||
```css
|
||||
.box {
|
||||
margin: 10px;
|
||||
border: 2px solid #005f8a;
|
||||
padding: 20px;
|
||||
width: 200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Responsive
|
||||
|
||||
```css
|
||||
@media (max-width: 768px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# JavaScript
|
||||
|
||||
---
|
||||
|
||||
# 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: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
1. Was ist der Unterschied zwischen `let` und `const`?
|
||||
2. Wie funktioniert `fetch()`?
|
||||
3. Was macht `box-sizing: border-box`?
|
||||
266
slides/dhbw/02_css_extended.md
Normal file
@@ -0,0 +1,266 @@
|
||||
---
|
||||
marp: true
|
||||
theme: gaia
|
||||
paginate: true
|
||||
backgroundColor: #fff
|
||||
header: "Web Engineering – DHBW Stuttgart"
|
||||
footer: "Michael Czechowski – SoSe 2025"
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-foreground: #1a1a2e;
|
||||
--color-highlight: #005f8a;
|
||||
--color-dimmed: #4a6a7a;
|
||||
}
|
||||
section.invert {
|
||||
--color-foreground: #fff;
|
||||
}
|
||||
section {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
h1 {
|
||||
color: #005f8a;
|
||||
}
|
||||
section.invert h1 {
|
||||
color: #fff;
|
||||
}
|
||||
pre {
|
||||
background: #0f1e2e;
|
||||
color: #00b4d8;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #005f8a;
|
||||
}
|
||||
pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
code {
|
||||
background: #1a3a4a;
|
||||
color: #00b4d8;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
a {
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# CSS: Extended
|
||||
|
||||
---
|
||||
|
||||
# CSS Selectors – Tag, Class, ID, Attribute
|
||||
|
||||
```css
|
||||
/* Tag */
|
||||
p { color: #333; }
|
||||
|
||||
/* Class */
|
||||
.button { background: blue; }
|
||||
|
||||
/* ID */
|
||||
#header { height: 60px; }
|
||||
|
||||
/* Attribute */
|
||||
input[type="text"] { border: 1px solid #ccc; }
|
||||
input[disabled] { background: #eee; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Selectors – Attribut-Selektoren
|
||||
|
||||
```css
|
||||
/* presence */
|
||||
[disabled] { opacity: 0.5; }
|
||||
|
||||
/* exact value */
|
||||
[type="email"] { border-color: blue; }
|
||||
|
||||
/* contains word */
|
||||
[class~="btn"] { cursor: pointer; }
|
||||
|
||||
/* starts with */
|
||||
[href^="https"] { color: green; }
|
||||
|
||||
/* ends with */
|
||||
[src$=".png"] { border: 1px solid #ccc; }
|
||||
|
||||
/* contains substring */
|
||||
[class*="icon"] { padding-left: 20px; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Combinators
|
||||
|
||||
```css
|
||||
/* Child (direkt) */
|
||||
div > p { margin: 0; }
|
||||
|
||||
/* Descendant (Nachkomme) */
|
||||
div p { margin: 0; }
|
||||
|
||||
/* Next Sibling */
|
||||
h1 + p { font-size: 1.2em; }
|
||||
|
||||
/* Subsequent Sibling */
|
||||
h1 ~ p { color: #666; }
|
||||
|
||||
/* Selector List */
|
||||
h1, h2, h3 { font-weight: bold; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Pseudo Classes
|
||||
|
||||
```css
|
||||
/* User Interaction */
|
||||
:hover { color: red; }
|
||||
:active { background: blue; }
|
||||
:focus { outline: 2px solid orange; }
|
||||
|
||||
/* Structure */
|
||||
:first-child { margin-left: 0; }
|
||||
:last-child { margin-right: 0; }
|
||||
:nth-child(odd) { background: #f9f9f9; }
|
||||
|
||||
/* Links */
|
||||
:visited { color: purple; }
|
||||
:link { color: blue; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Pseudo Classes – More
|
||||
|
||||
```css
|
||||
/* Form States */
|
||||
:valid { border-color: green; }
|
||||
:invalid { border-color: red; }
|
||||
:placeholder-shown { color: #999; }
|
||||
|
||||
/* Content */
|
||||
:before { content: "→ "; }
|
||||
:after { content: " [Wichtig]"; }
|
||||
:first-letter { font-size: 2em; }
|
||||
:first-line { font-weight: bold; }
|
||||
|
||||
/* NOT */
|
||||
:not(.disabled) { pointer-events: auto; }
|
||||
:not(:last-child) { border-bottom: 1px solid #ccc; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Shorthand Properties
|
||||
|
||||
```css
|
||||
/* margin: top right bottom left */
|
||||
margin: 10px 20px 10px 20px;
|
||||
|
||||
/* centering */
|
||||
margin: 0 auto;
|
||||
|
||||
/* padding same */
|
||||
padding: 20px;
|
||||
|
||||
/* border: width style color */
|
||||
border: 1px solid #333;
|
||||
|
||||
/* background: color image position/size repeat */
|
||||
background: #fff url(logo.png) 0 0 no-repeat;
|
||||
|
||||
/* font: style variant weight size/line-height family */
|
||||
font: italic normal bold 16px/1.5 Arial, sans-serif;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Flexbox Shorthands
|
||||
|
||||
```css
|
||||
/* flex: grow shrink basis */
|
||||
flex: 1 0 auto;
|
||||
|
||||
/* place-items: align-items justify-items */
|
||||
place-items: center stretch;
|
||||
|
||||
/* place-content: align-content justify-content */
|
||||
place-content: space-between center;
|
||||
|
||||
/* gap: row-gap column-gap */
|
||||
gap: 20px 40px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Custom Properties (Variables)
|
||||
|
||||
```css
|
||||
:root {
|
||||
--primary-color: #005f8a;
|
||||
--spacing: 20px;
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: var(--primary-color);
|
||||
padding: var(--spacing);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CSS Functions
|
||||
|
||||
```css
|
||||
/* calc */
|
||||
width: calc(100% - 40px);
|
||||
|
||||
/* min/max/clamp */
|
||||
width: min(100%, 600px);
|
||||
font-size: clamp(14px, 2vw, 20px);
|
||||
|
||||
/* var with fallback */
|
||||
color: var(--text-color, #333);
|
||||
|
||||
/* rgb/rgba/hsl/hsla */
|
||||
color: hsl(200, 100%, 50%);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Transition & Animation
|
||||
|
||||
```css
|
||||
/* transition: property duration timing-function */
|
||||
transition: all 0.3s ease-in-out;
|
||||
transition: background 0.2s, transform 0.1s;
|
||||
|
||||
/* animation */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-out;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
1. Was bedeutet `div > p` vs `div p`?
|
||||
2. Wann nutzt du `nth-child(even)`?
|
||||
3. Warum `--variable` statt direkt `#fff`?
|
||||
228
slides/dhbw/03_nodejs_basics.md
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
marp: true
|
||||
theme: gaia
|
||||
paginate: true
|
||||
backgroundColor: #fff
|
||||
header: "Web Engineering – DHBW Stuttgart"
|
||||
footer: "Michael Czechowski – SoSe 2025"
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-foreground: #1a1a2e;
|
||||
--color-highlight: #005f8a;
|
||||
--color-dimmed: #4a6a7a;
|
||||
}
|
||||
section.invert {
|
||||
--color-foreground: #fff;
|
||||
}
|
||||
section {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
h1 {
|
||||
color: #005f8a;
|
||||
}
|
||||
section.invert h1 {
|
||||
color: #fff;
|
||||
}
|
||||
pre {
|
||||
background: #0f1e2e;
|
||||
color: #00b4d8;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #005f8a;
|
||||
}
|
||||
pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
code {
|
||||
background: #1a3a4a;
|
||||
color: #00b4d8;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
a {
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# 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: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# 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
|
||||
node -e "console.log(require('fs').readFileSync('data.json'))"
|
||||
|
||||
# JSON parsen
|
||||
node -e "console.log(JSON.parse(process.argv[1]))" '{"a":1}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# require() – Module laden
|
||||
|
||||
```javascript
|
||||
// Eigene Datei
|
||||
const utils = require('./utils');
|
||||
|
||||
// npm Modul
|
||||
const express = require('express');
|
||||
|
||||
// Built-in
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# __dirname und __filename
|
||||
|
||||
```javascript
|
||||
// Aktuelles Verzeichnis (Node.js)
|
||||
console.log(__dirname); // /home/user/project/src
|
||||
console.log(__filename); // /home/user/project/src/app.js
|
||||
|
||||
// Pfad joinen
|
||||
const configPath = path.join(__dirname, '..', 'config.json');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# HTTP Server mit Node.js (Built-in)
|
||||
|
||||
```javascript
|
||||
const http = require('http');
|
||||
|
||||
const server = http.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');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# fs – Dateien lesen/schreiben
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
|
||||
// Async
|
||||
fs.readFile('data.json', 'utf8', (err, data) => {
|
||||
if (err) throw err;
|
||||
console.log(JSON.parse(data));
|
||||
});
|
||||
|
||||
// Sync
|
||||
const content = fs.readFileSync('data.json', 'utf8');
|
||||
|
||||
// Write
|
||||
fs.writeFile('output.txt', 'Hallo Welt', (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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
|
||||
|
||||
```javascript
|
||||
// utils.js
|
||||
module.exports = {
|
||||
add: (a, b) => a + b,
|
||||
greet: (name) => `Hallo ${name}!`
|
||||
};
|
||||
|
||||
// Oder einzelne Exporte
|
||||
module.exports.add = (a, b) => a + b;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
1. Wie startest du einen Node.js Server auf Port 8080?
|
||||
2. Was macht `node --check`?
|
||||
3. Wozu dient `package.json`?
|
||||
258
slides/dhbw/04_nodejs_advanced.md
Normal file
@@ -0,0 +1,258 @@
|
||||
---
|
||||
marp: true
|
||||
theme: gaia
|
||||
paginate: true
|
||||
backgroundColor: #fff
|
||||
header: "Web Engineering – DHBW Stuttgart"
|
||||
footer: "Michael Czechowski – SoSe 2025"
|
||||
---
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-foreground: #1a1a2e;
|
||||
--color-highlight: #005f8a;
|
||||
--color-dimmed: #4a6a7a;
|
||||
}
|
||||
section.invert {
|
||||
--color-foreground: #fff;
|
||||
}
|
||||
section {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
h1 {
|
||||
color: #005f8a;
|
||||
}
|
||||
section.invert h1 {
|
||||
color: #fff;
|
||||
}
|
||||
pre {
|
||||
background: #0f1e2e;
|
||||
color: #00b4d8;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #005f8a;
|
||||
}
|
||||
pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
code {
|
||||
background: #1a3a4a;
|
||||
color: #00b4d8;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
a {
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- _class: invert -->
|
||||
<!-- _header: '' -->
|
||||
<!-- _backgroundColor: #001520 -->
|
||||
|
||||
# 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
|
||||
import express from 'express';
|
||||
import { port } from './src/config/constants.js';
|
||||
|
||||
// CommonJS
|
||||
const fs = require('fs');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 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: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# 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: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
<!-- _header: '' -->
|
||||
<!-- _footer: '' -->
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
|
||||
# Fragen?
|
||||
|
||||
1. Wozu dient `process.on('SIGINT')`?
|
||||
2. Was macht `app.use(express.json())`?
|
||||
3. Wie erstellst du einen GET-Endpoint mit Express?
|
||||
0
slides/dhbw/assets/.gitkeep
Normal file
146
slides/dhbw/assets/demos/browser-server-sequence.html
Normal file
@@ -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>
|
||||
BIN
slides/dhbw/assets/demos/browser-server-sequence.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
167
slides/dhbw/assets/demos/docker-orchestration.html
Normal file
@@ -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>
|
||||
BIN
slides/dhbw/assets/demos/docker-orchestration.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
105
slides/dhbw/assets/demos/js-frameworks-overview.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>JavaScript Frameworks – Übersicht</title>
|
||||
<style>
|
||||
:root {
|
||||
--dark: #1a1a2e;
|
||||
--muted: #6b7280;
|
||||
}
|
||||
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 28px; 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; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }
|
||||
|
||||
.col {
|
||||
border-radius: 12px; padding: 18px 12px 16px;
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.col.active { outline: 2px solid var(--dark); outline-offset: 2px; }
|
||||
|
||||
.col-title {
|
||||
font-size: 0.78rem; font-weight: 700; text-transform: uppercase;
|
||||
letter-spacing: 0.06em; color: var(--dark); text-align: center;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.12); padding-bottom: 8px; margin-bottom: 2px;
|
||||
}
|
||||
.items { display: flex; flex-direction: column; gap: 6px; }
|
||||
.item {
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
font-size: 0.82rem; font-weight: 600;
|
||||
}
|
||||
.item .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
||||
.item .note { font-size: 0.7rem; font-weight: 400; color: var(--muted); display: block; }
|
||||
|
||||
.css-col { background: #f1f5f9; }
|
||||
.fe-col { background: #fce7f3; }
|
||||
.render-col { background: #ede9fe; }
|
||||
.build-col { background: #dbeafe; }
|
||||
.be-col { background: #d1fae5; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JavaScript Frameworks – Übersicht</h1>
|
||||
<p class="sub">Kategorien des modernen JS-Ökosystems</p>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<div class="col css-col">
|
||||
<div class="col-title">CSS Frameworks</div>
|
||||
<div class="items">
|
||||
<div class="item"><div class="dot" style="background:#38bdf8"></div><span>Tailwind CSS<span class="note">Utility-first</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#7c3aed"></div><span>Bootstrap<span class="note">Komponenten</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#e879f9"></div><span>Pico CSS<span class="note">Classless</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#f97316"></div><span>UnoCSS<span class="note">Atomic</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col fe-col">
|
||||
<div class="col-title">Frontend Frameworks</div>
|
||||
<div class="items">
|
||||
<div class="item"><div class="dot" style="background:#61dafb"></div><span>React<span class="note">Meta / Virtual DOM</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#ff3e00"></div><span>Svelte<span class="note">Compiler-based</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#42b883"></div><span>Vue<span class="note">Progressive</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#dd0031"></div><span>Angular<span class="note">Google / Full</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col render-col">
|
||||
<div class="col-title">Rendering Frameworks</div>
|
||||
<div class="items">
|
||||
<div class="item"><div class="dot" style="background:#000"></div><span>Next.js<span class="note">React / SSR</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#ff3e00"></div><span>SvelteKit<span class="note">Svelte / SSR</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#42b883"></div><span>Nuxt<span class="note">Vue / SSR</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#ff5d01"></div><span>Astro<span class="note">Islands Arch.</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#0ea5e9"></div><span>Gatsby<span class="note">React / SSG</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col build-col">
|
||||
<div class="col-title">Build Tools & Bundler</div>
|
||||
<div class="items">
|
||||
<div class="item"><div class="dot" style="background:#646cff"></div><span>Vite<span class="note">Dev-Server / HMR</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#2b3a42"></div><span>Webpack<span class="note">Bundler</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#f9b640"></div><span>esbuild<span class="note">Go / ultra-fast</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#3178c6"></div><span>tsc<span class="note">TypeScript</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#cc3534"></div><span>Rollup<span class="note">Libraries</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col be-col active">
|
||||
<div class="col-title">Backend Frameworks</div>
|
||||
<div class="items">
|
||||
<div class="item"><div class="dot" style="background:#68a063"></div><span>Express<span class="note">Minimal / de-facto</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#3178c6"></div><span>Fastify<span class="note">Performant</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#5a45ff"></div><span>AdonisJS<span class="note">Full-stack</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#e11d48"></div><span>NestJS<span class="note">Angular-style</span></span></div>
|
||||
<div class="item"><div class="dot" style="background:#000"></div><span>Hono<span class="note">Edge-ready</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
slides/dhbw/assets/demos/js-frameworks-overview.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
85
slides/dhbw/assets/demos/nodejs-usecases.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Node.js – Einsatzgebiete</title>
|
||||
<style>
|
||||
:root {
|
||||
--dark: #1a1a2e;
|
||||
--muted: #6b7280;
|
||||
}
|
||||
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 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: 28px; font-size: 0.88rem; text-align: center; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
|
||||
|
||||
.card {
|
||||
border-radius: 12px; padding: 28px 16px 24px;
|
||||
display: flex; flex-direction: column; align-items: center; gap: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.card .icon { font-size: 2rem; line-height: 1; }
|
||||
.card .title { font-size: 1.1rem; font-weight: 700; }
|
||||
.card .desc { font-size: 0.8rem; color: var(--muted); line-height: 1.5; }
|
||||
.card .tags { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; margin-top: 4px; }
|
||||
.tag { font-size: 0.72rem; background: rgba(0,0,0,0.07); border-radius: 99px; padding: 2px 9px; font-family: ui-monospace, monospace; }
|
||||
|
||||
.cli { background: #fce7f3; }
|
||||
.api { background: #ede9fe; }
|
||||
.web { background: #dbeafe; }
|
||||
.ws { background: #d1fae5; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Node.js – Einsatzgebiete</h1>
|
||||
<p class="sub">Vier Hauptkategorien serverseitiger JavaScript-Anwendungen</p>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card cli">
|
||||
<div class="icon">⌨️</div>
|
||||
<div class="title">CLI</div>
|
||||
<div class="desc">Kommandozeilen-Tools, Skripte, Build-Prozesse, Automatisierung</div>
|
||||
<div class="tags">
|
||||
<span class="tag">npm scripts</span>
|
||||
<span class="tag">process.argv</span>
|
||||
<span class="tag">fs</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card api">
|
||||
<div class="icon">🔌</div>
|
||||
<div class="title">API Server</div>
|
||||
<div class="desc">REST- und GraphQL-APIs, JSON-Endpunkte, CRUD-Operationen</div>
|
||||
<div class="tags">
|
||||
<span class="tag">express</span>
|
||||
<span class="tag">GET/POST</span>
|
||||
<span class="tag">JSON</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card web">
|
||||
<div class="icon">🌐</div>
|
||||
<div class="title">Web Server</div>
|
||||
<div class="desc">Statische Dateien ausliefern, Server-Side Rendering, Reverse Proxy</div>
|
||||
<div class="tags">
|
||||
<span class="tag">http</span>
|
||||
<span class="tag">SSR</span>
|
||||
<span class="tag">next.js</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card ws">
|
||||
<div class="icon">⚡</div>
|
||||
<div class="title">Web Sockets</div>
|
||||
<div class="desc">Echtzeit-Kommunikation, Chat, Live-Updates, bidirektional</div>
|
||||
<div class="tags">
|
||||
<span class="tag">ws</span>
|
||||
<span class="tag">socket.io</span>
|
||||
<span class="tag">events</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
slides/dhbw/assets/demos/nodejs-usecases.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
63
slides/dhbw/assets/demos/test-automation-trophy.html
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Automation Trophy</title>
|
||||
<style>
|
||||
:root {
|
||||
--dark: #1a1a2e;
|
||||
--muted: #6b7280;
|
||||
--border: #cbd5e1;
|
||||
--bg-soft: #f8fafc;
|
||||
}
|
||||
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 32px; display: flex; flex-direction: column; align-items: center; }
|
||||
h1 { margin: 0 0 4px; font-size: 1.55rem; text-align: center; }
|
||||
.sub { color: var(--muted); margin-bottom: 28px; font-size: 0.92rem; text-align: center; }
|
||||
|
||||
.trophy { display: flex; flex-direction: column; align-items: center; gap: 0; width: 480px; }
|
||||
|
||||
.layer {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-weight: 700; font-size: 1.05rem; color: var(--dark);
|
||||
border-radius: 6px; transition: opacity 0.2s;
|
||||
}
|
||||
.layer .count {
|
||||
font-size: 0.78rem; font-weight: 400; color: var(--muted);
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.e2e { background: #fca5a5; width: 40%; height: 54px; clip-path: polygon(10% 100%, 90% 100%, 100% 0%, 0% 0%); }
|
||||
.integration{ background: #60a5fa; width: 80%; height: 86px; }
|
||||
.unit { background: #c4b5fd; width: 40%; height: 54px; clip-path: polygon(0% 100%, 100% 100%, 85% 0%, 15% 0%); }
|
||||
.static { background: #fde68a; width: 28%; height: 60px; clip-path: polygon(0% 0%, 100% 0%, 80% 100%, 20% 100%); }
|
||||
|
||||
.legend {
|
||||
margin-top: 28px; display: grid; grid-template-columns: 1fr 1fr;
|
||||
gap: 8px 24px; font-size: 0.88rem; width: 480px;
|
||||
}
|
||||
.leg { display: flex; align-items: center; gap: 10px; }
|
||||
.swatch { width: 16px; height: 16px; border-radius: 3px; flex-shrink: 0; }
|
||||
.leg strong { display: block; }
|
||||
.leg span { color: var(--muted); font-size: 0.82rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Automation Trophy</h1>
|
||||
<p class="sub">Schicht-Modell nach Kent C. Dodds — Breite = empfohlener Anteil</p>
|
||||
|
||||
<div class="trophy">
|
||||
<div class="layer e2e">E2E</div>
|
||||
<div class="layer integration">Integration</div>
|
||||
<div class="layer unit">Unit</div>
|
||||
<div class="layer static">Static</div>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="leg"><div class="swatch" style="background:#fde68a"></div><div><strong>Static</strong><span>Linting, Type-Check, Format</span></div></div>
|
||||
<div class="leg"><div class="swatch" style="background:#c4b5fd"></div><div><strong>Unit</strong><span>Einzelne Funktionen / Komponenten</span></div></div>
|
||||
<div class="leg"><div class="swatch" style="background:#60a5fa"></div><div><strong>Integration</strong><span>Zusammenspiel mehrerer Teile ← Fokus</span></div></div>
|
||||
<div class="leg"><div class="swatch" style="background:#fca5a5"></div><div><strong>End to End</strong><span>Ganzer User-Flow im Browser</span></div></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
slides/dhbw/assets/demos/test-automation-trophy.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
124
slides/dhbw/assets/demos/ts-js-compilation.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>TypeScript → JavaScript Compilation</title>
|
||||
<style>
|
||||
:root {
|
||||
--dark: #1a1a2e;
|
||||
--muted: #6b7280;
|
||||
--border: #cbd5e1;
|
||||
--bg-soft: #f8fafc;
|
||||
--ts: #3178c6;
|
||||
--js: #f7df1e;
|
||||
--remove: #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: 24px 28px; 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; }
|
||||
|
||||
.panels { display: grid; grid-template-columns: 1fr 48px 1fr 48px 1fr; align-items: start; gap: 0; }
|
||||
|
||||
.panel { border: 2px solid var(--border); border-radius: 10px; overflow: hidden; }
|
||||
.panel-head {
|
||||
padding: 8px 14px; font-size: 0.78rem; font-weight: 700;
|
||||
text-transform: uppercase; letter-spacing: 0.07em;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.panel-body { padding: 14px; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; }
|
||||
|
||||
.ts .panel-head { background: var(--ts); color: #fff; }
|
||||
.strip .panel-head { background: #fee2e2; color: var(--remove); }
|
||||
.js .panel-head { background: var(--js); color: var(--dark); }
|
||||
|
||||
pre {
|
||||
margin: 0; font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
||||
font-size: 0.78rem; line-height: 1.65; white-space: pre;
|
||||
}
|
||||
.removed { color: var(--remove); text-decoration: line-through; background: #fee2e2; border-radius: 2px; }
|
||||
.kept { color: #166534; }
|
||||
.kw { color: var(--ts); }
|
||||
.kw-js { color: #ca8a04; }
|
||||
.type-ann{ color: #7c3aed; }
|
||||
.str { color: #16a34a; }
|
||||
.fn { color: #0369a1; }
|
||||
|
||||
.arrow-col {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
justify-content: center; padding-top: 52px; gap: 4px;
|
||||
}
|
||||
.arrow-shaft { width: 2px; height: 32px; background: var(--muted); }
|
||||
.arrow-head { width: 0; height: 0; border-left: 7px solid transparent; border-right: 7px solid transparent; border-top: 10px solid var(--muted); }
|
||||
.arrow-label { font-size: 0.68rem; color: var(--muted); writing-mode: vertical-rl; text-orientation: mixed; letter-spacing: 0.05em; margin-top: 4px; white-space: nowrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>TypeScript → JavaScript Compilation</h1>
|
||||
<p class="sub">Der TypeScript-Compiler entfernt alle Typ-Annotationen — nur JavaScript bleibt</p>
|
||||
|
||||
<div class="panels">
|
||||
|
||||
<!-- TypeScript -->
|
||||
<div class="panel ts">
|
||||
<div class="panel-head">📘 TypeScript-Datei</div>
|
||||
<div class="panel-body">
|
||||
<pre><span class="kw">type</span> <span class="type-ann">Result</span> = <span class="str">"pass"</span> | <span class="str">"fail"</span>;
|
||||
|
||||
<span class="kw">function</span> <span class="fn">verify</span>(result: <span class="type-ann">Result</span>) {
|
||||
<span class="kw">if</span> (result === <span class="str">"pass"</span>) {
|
||||
console.<span class="fn">log</span>(<span class="str">"Passed"</span>);
|
||||
} <span class="kw">else</span> {
|
||||
console.<span class="fn">log</span>(<span class="str">"Failed"</span>);
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Arrow 1 -->
|
||||
<div class="arrow-col">
|
||||
<div class="arrow-shaft"></div>
|
||||
<div class="arrow-head"></div>
|
||||
<div class="arrow-label">tsc</div>
|
||||
</div>
|
||||
|
||||
<!-- Strip phase -->
|
||||
<div class="panel strip">
|
||||
<div class="panel-head">✂️ Typen werden entfernt</div>
|
||||
<div class="panel-body">
|
||||
<pre><span class="removed">type Result = "pass" | "fail";</span>
|
||||
|
||||
<span class="kw">function</span> <span class="fn">verify</span>(result<span class="removed">: Result</span>) {
|
||||
<span class="kw">if</span> (result === <span class="str">"pass"</span>) {
|
||||
console.<span class="fn">log</span>(<span class="str">"Passed"</span>);
|
||||
} <span class="kw">else</span> {
|
||||
console.<span class="fn">log</span>(<span class="str">"Failed"</span>);
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Arrow 2 -->
|
||||
<div class="arrow-col">
|
||||
<div class="arrow-shaft"></div>
|
||||
<div class="arrow-head"></div>
|
||||
<div class="arrow-label">output</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<div class="panel js">
|
||||
<div class="panel-head">📄 JavaScript-Datei</div>
|
||||
<div class="panel-body">
|
||||
<pre><span class="kw-js">function</span> <span class="fn">verify</span>(result) {
|
||||
<span class="kw-js">if</span> (result === <span class="str">"pass"</span>) {
|
||||
console.<span class="fn">log</span>(<span class="str">"Passed"</span>);
|
||||
} <span class="kw-js">else</span> {
|
||||
console.<span class="fn">log</span>(<span class="str">"Failed"</span>);
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
slides/dhbw/assets/demos/ts-js-compilation.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
107
slides/dhbw/assets/demos/web-ecosystem-rings.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Web Ecosystem – Schichten</title>
|
||||
<style>
|
||||
:root {
|
||||
--dark: #1a1a2e;
|
||||
--muted: #6b7280;
|
||||
}
|
||||
html, body { margin: 0; padding: 0; background: #fff; min-height: 100vh; font-family: system-ui, -apple-system, sans-serif; color: var(--dark); }
|
||||
body { padding: 20px 28px; display: flex; flex-direction: column; align-items: center; }
|
||||
h1 { margin: 0 0 4px; font-size: 1.45rem; text-align: center; }
|
||||
.sub { color: var(--muted); margin-bottom: 20px; font-size: 0.88rem; text-align: center; }
|
||||
|
||||
.rings-wrap { position: relative; width: 520px; height: 520px; }
|
||||
|
||||
.ring {
|
||||
position: absolute; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* layers from outside in */
|
||||
.r-css-libs { width: 520px; height: 520px; background: #f0fdf4; border: 2px solid #86efac; }
|
||||
.r-frameworks{ width: 400px; height: 400px; background: #eff6ff; border: 2px solid #93c5fd; }
|
||||
.r-bundlers { width: 290px; height: 290px; background: #fdf4ff; border: 2px solid #d8b4fe; }
|
||||
.r-postcss { width: 196px; height: 196px; background: #fff7ed; border: 2px solid #fdba74; }
|
||||
.r-core { width: 116px; height: 116px; background: var(--dark); border: none; color: #fff; flex-direction: column; gap: 2px; text-align: center; }
|
||||
.r-core span { font-size: 0.72rem; font-weight: 700; line-height: 1.3; }
|
||||
|
||||
/* floating labels */
|
||||
.label {
|
||||
position: absolute; font-size: 0.72rem; font-weight: 700;
|
||||
text-transform: uppercase; letter-spacing: 0.06em;
|
||||
background: rgba(255,255,255,0.85); padding: 2px 7px;
|
||||
border-radius: 99px; white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ring label positions — top of each ring */
|
||||
.lbl-css-libs { top: 12px; left: 50%; transform: translateX(-50%); color: #16a34a; }
|
||||
.lbl-frameworks { top: 72px; left: 50%; transform: translateX(-50%); color: #2563eb; }
|
||||
.lbl-bundlers { top: 128px; left: 50%; transform: translateX(-50%); color: #7c3aed; }
|
||||
.lbl-postcss { top: 178px; left: 50%; transform: translateX(-50%); color: #ea580c; }
|
||||
|
||||
/* item pills scattered in each ring band */
|
||||
.pill {
|
||||
position: absolute; font-size: 0.7rem; font-weight: 600;
|
||||
padding: 3px 9px; border-radius: 99px; white-space: nowrap;
|
||||
background: rgba(255,255,255,0.9); border: 1px solid rgba(0,0,0,0.10);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Web Ecosystem – Schichten</h1>
|
||||
<p class="sub">Von innen nach außen: Kern → Standards → Werkzeuge → Frameworks → CSS-Utilities</p>
|
||||
|
||||
<div class="rings-wrap">
|
||||
|
||||
<!-- Rings (painted back-to-front) -->
|
||||
<div class="ring r-css-libs"></div>
|
||||
<div class="ring r-frameworks"></div>
|
||||
<div class="ring r-bundlers"></div>
|
||||
<div class="ring r-postcss"></div>
|
||||
<div class="ring r-core">
|
||||
<span>JS</span>
|
||||
<span>HTML</span>
|
||||
<span>CSS</span>
|
||||
</div>
|
||||
|
||||
<!-- Ring labels -->
|
||||
<div class="label lbl-css-libs">CSS Utilities & Libraries</div>
|
||||
<div class="label lbl-frameworks">Frontend / Backend Frameworks</div>
|
||||
<div class="label lbl-bundlers">Bundler & Build Tools</div>
|
||||
<div class="label lbl-postcss">PostCSS / Preprocessors</div>
|
||||
|
||||
<!-- CSS Libs band (outer ring) -->
|
||||
<div class="pill" style="top:52px; left:30px; color:#16a34a;">Tailwind</div>
|
||||
<div class="pill" style="top:60px; right:24px; color:#16a34a;">Bootstrap</div>
|
||||
<div class="pill" style="bottom:52px;left:38px; color:#16a34a;">shadcn/ui</div>
|
||||
<div class="pill" style="bottom:56px;right:28px;color:#16a34a;">Pico CSS</div>
|
||||
<div class="pill" style="top:50%; left:4px; transform:translateY(-50%); color:#16a34a;">UnoCSS</div>
|
||||
<div class="pill" style="top:50%; right:4px; transform:translateY(-50%); color:#16a34a;">DaisyUI</div>
|
||||
|
||||
<!-- Frameworks band -->
|
||||
<div class="pill" style="top:88px; left:86px; color:#2563eb;">React</div>
|
||||
<div class="pill" style="top:88px; right:72px; color:#2563eb;">Vue</div>
|
||||
<div class="pill" style="bottom:88px;left:72px; color:#2563eb;">Svelte</div>
|
||||
<div class="pill" style="bottom:88px;right:60px;color:#2563eb;">Angular</div>
|
||||
<div class="pill" style="top:42%; left:60px; color:#2563eb;">Next.js</div>
|
||||
<div class="pill" style="top:56%; left:60px; color:#2563eb;">Express</div>
|
||||
<div class="pill" style="top:46%; right:54px; color:#2563eb;">Nuxt</div>
|
||||
<div class="pill" style="top:58%; right:54px; color:#2563eb;">Fastify</div>
|
||||
|
||||
<!-- Bundlers band -->
|
||||
<div class="pill" style="top:148px; left:145px; color:#7c3aed;">Vite</div>
|
||||
<div class="pill" style="top:148px; right:132px;color:#7c3aed;">Webpack</div>
|
||||
<div class="pill" style="bottom:145px;left:134px;color:#7c3aed;">esbuild</div>
|
||||
<div class="pill" style="bottom:145px;right:120px;color:#7c3aed;">Rollup</div>
|
||||
|
||||
<!-- PostCSS band -->
|
||||
<div class="pill" style="top:196px; left:50%; transform:translateX(-50%); color:#ea580c;">PostCSS</div>
|
||||
<div class="pill" style="bottom:196px;left:50%;transform:translateX(-50%); color:#ea580c;">Sass / SCSS</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
slides/dhbw/assets/demos/web-ecosystem-rings.png
Normal file
|
After Width: | Height: | Size: 218 KiB |