dhbw: neuer kurs technik I — 4 kapitel + demo assets

This commit is contained in:
2026-05-04 20:05:46 +02:00
parent 5c419c9ed1
commit 841a7ced76
19 changed files with 1967 additions and 0 deletions

418
slides/dhbw/01_web_eng.md Normal file
View 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 -->
![bg cover opacity:0.2](./assets/background.png)
# 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: '' -->
![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"}, ...]
```
---
# 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: '' -->
![bg fit](./assets/demos/web-ecosystem-rings.png)
---
# Fragen?
1. Was ist der Unterschied zwischen `let` und `const`?
2. Wie funktioniert `fetch()`?
3. Was macht `box-sizing: border-box`?

View 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`?

View 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: '' -->
![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
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`?

View 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: '' -->
![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)
---
# Fragen?
1. Wozu dient `process.on('SIGINT')`?
2. Was macht `app.use(express.json())`?
3. Wie erstellst du einen GET-Endpoint mit Express?

View File

View File

@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>BrowserServer 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>BrowserServer: 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: 98 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View 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 &amp; 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View 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 &amp; Libraries</div>
<div class="label lbl-frameworks">Frontend / Backend Frameworks</div>
<div class="label lbl-bundlers">Bundler &amp; 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB