per user-request einen branch der main-content UND rewrite-content trägt — saubere separierung statt symlink/merge-conflict. struktur: - slides/<course>/ → main-content restored, eigene assets (kein new-demo-leak) - slides/<course>-2026/ → rewrite-content, eigene assets (komplett kopiert + new demos) Makefile: COURSES += 223015b-2026 223015c-2026 dhbw-2026 mit eigenen NAME/KAPITEL/DEPLOY/KLAUSUR-konfig. Klausur für b+c=1, dhbw=leer. Deploy-paths /hdm/223015b-2026 etc. make build durchlaufen für alle 6 decks, 0 fehler. dedup: 223015c+dhbw assets identisch in beiden ordnern (kein neu-demos auf b ergänzt). 223015b 86 → 120 files in -2026 (= +34 neue eroeffner/usb-c-drei-achsen/container-vs-codec/etc).
454 lines
9.6 KiB
Markdown
454 lines
9.6 KiB
Markdown
---
|
||
marp: true
|
||
theme: gaia
|
||
paginate: true
|
||
backgroundColor: #fff
|
||
header: "Web Engineering – DHBW Stuttgart"
|
||
footer: "Michael Czechowski – SoSe 2026"
|
||
---
|
||
|
||
<style>
|
||
:root { --color-foreground: #1a1a2e; --color-highlight: #d63384; --color-dimmed: #4a4a6a; }
|
||
section.invert { --color-foreground: #fff; }
|
||
section { font-size: 1.4rem; }
|
||
h1 { color: #a02060; }
|
||
section.invert h1 { color: #fff; }
|
||
h2 { color: #1f2937; }
|
||
section.invert h2 { color: #f48fb1; }
|
||
pre { background: #0f0f23; color: #f48fb1; border-radius: 8px; border-left: 3px solid #d63384; }
|
||
pre code { background: transparent; color: inherit; }
|
||
code { background: #1a1a2e; color: #f48fb1; padding: 0.15em 0.4em; border-radius: 4px; }
|
||
a { color: var(--color-highlight); }
|
||
</style>
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Kapitel 2 — CSS Extended
|
||
## Box-Model · Flexbox · Grid · Responsive · Animations
|
||
|
||
---
|
||
|
||
# Recap Kap 1
|
||
|
||
| Bereich | Was wir kennen |
|
||
|---------|----------------|
|
||
| Anatomie | `selector { property: value }` |
|
||
| Selektoren | element, `.class`, `#id`, `:hover` |
|
||
| Spezifizität | inline > id > class > element |
|
||
| Farben | hex, rgb, hsl, oklch |
|
||
| Einheiten | px, rem, em, %, vw, vh |
|
||
|
||
**Heute:** Layout + Animation in Tiefe.
|
||
|
||
---
|
||
|
||
# Box-Model — die Erinnerung
|
||
|
||
```
|
||
┌──────────────────┐
|
||
│ margin │
|
||
│ ┌────────────┐ │
|
||
│ │ border │ │
|
||
│ │ ┌────────┐ │ │
|
||
│ │ │padding │ │ │
|
||
│ │ │content │ │ │
|
||
│ │ └────────┘ │ │
|
||
│ └────────────┘ │
|
||
└──────────────────┘
|
||
```
|
||
|
||
`box-sizing: border-box` global setzen.
|
||
|
||
---
|
||
|
||
# Box-Model in Code
|
||
|
||
```css
|
||
* { box-sizing: border-box; }
|
||
|
||
.card {
|
||
width: 320px; /* inkl. padding + border */
|
||
padding: 1.5rem;
|
||
border: 2px solid #ccc;
|
||
border-radius: 0.75rem;
|
||
margin: 1rem auto; /* Außen + horizontal zentriert */
|
||
}
|
||
```
|
||
|
||
Inspector im DevTools zeigt das Box-Model live an.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Flexbox
|
||
## 1D-Layout (Reihe ODER Spalte)
|
||
|
||
---
|
||
|
||
# Flexbox — Basics
|
||
|
||
```css
|
||
.container {
|
||
display: flex;
|
||
gap: 1rem; /* Abstand zwischen Items */
|
||
flex-direction: row; /* row | column */
|
||
justify-content: space-between; /* Haupt-Achse */
|
||
align-items: center; /* Quer-Achse */
|
||
flex-wrap: wrap; /* umbruchen erlaubt */
|
||
}
|
||
```
|
||
|
||
```html
|
||
<div class="container">
|
||
<div>1</div>
|
||
<div>2</div>
|
||
<div>3</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
# Flexbox — Achsen
|
||
|
||
**`flex-direction: row` (default):** Haupt-Achse horizontal.
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ ◄── justify-content ──► │
|
||
│ ▲ │
|
||
│ │ │
|
||
│ align-items │
|
||
│ │ │
|
||
│ ▼ │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
`column`: Achsen drehen sich 90°.
|
||
|
||
---
|
||
|
||
# Flexbox — Item-Properties
|
||
|
||
```css
|
||
.item {
|
||
flex: 1; /* nimm gleiche Anteile vom verfügbaren Raum */
|
||
flex: 0 0 200px; /* fest 200px, kein wachsen, kein schrumpfen */
|
||
flex-grow: 2; /* doppelte Anteile */
|
||
order: -1; /* visuell zuerst, im DOM hinten */
|
||
align-self: end; /* override align-items für DIESES Item */
|
||
}
|
||
```
|
||
|
||
**Shorthand:** `flex: <grow> <shrink> <basis>`.
|
||
|
||
---
|
||
|
||
# Typische Flex-Patterns
|
||
|
||
```css
|
||
/* Navigation: Logo links, Items rechts */
|
||
.nav { display: flex; justify-content: space-between; }
|
||
|
||
/* Karte: Bild + Text nebeneinander */
|
||
.card { display: flex; gap: 1rem; align-items: center; }
|
||
|
||
/* Button-Gruppe: alle gleich groß */
|
||
.btn-group { display: flex; gap: 0.5rem; }
|
||
.btn-group button { flex: 1; }
|
||
|
||
/* Footer: gleichmäßig verteilt */
|
||
.footer { display: flex; justify-content: space-around; }
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Grid
|
||
## 2D-Layout (Reihe UND Spalte)
|
||
|
||
---
|
||
|
||
# Grid — Basics
|
||
|
||
```css
|
||
.container {
|
||
display: grid;
|
||
grid-template-columns: 1fr 2fr 1fr; /* 3 Spalten, Verhältnis 1:2:1 */
|
||
grid-template-rows: auto 1fr auto; /* Header, Body, Footer */
|
||
gap: 1rem;
|
||
}
|
||
```
|
||
|
||
`fr` = Fraction Unit — verbleibender Raum aufgeteilt.
|
||
|
||
---
|
||
|
||
# Grid — Areas (named layout)
|
||
|
||
```css
|
||
.layout {
|
||
display: grid;
|
||
grid-template-areas:
|
||
"header header"
|
||
"nav main"
|
||
"footer footer";
|
||
grid-template-columns: 200px 1fr;
|
||
}
|
||
|
||
.layout header { grid-area: header; }
|
||
.layout nav { grid-area: nav; }
|
||
.layout main { grid-area: main; }
|
||
.layout footer { grid-area: footer; }
|
||
```
|
||
|
||
Lesbarer als Spalten-/Zeilen-Nummern.
|
||
|
||
---
|
||
|
||
# Grid — Auto-Layout
|
||
|
||
```css
|
||
/* So viele Spalten wie reinpassen, jede min. 200px */
|
||
.gallery {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 1rem;
|
||
}
|
||
```
|
||
|
||
Mobile: 1 Spalte. Tablet: 2-3. Desktop: 4-5. **Ohne Media Queries.**
|
||
|
||
---
|
||
|
||
# Flexbox vs. Grid
|
||
|
||
| | Flexbox | Grid |
|
||
|--|---------|------|
|
||
| Dimension | 1D | 2D |
|
||
| Use-Case | Navigation, Karten-Reihe, Button-Gruppe | Seiten-Layout, komplexes Raster |
|
||
| Item-Größe | flexibel | meist fest |
|
||
| Direction | row/column | rows + columns |
|
||
|
||
**Beide in einem Layout möglich:** Grid für Seite, Flexbox in Komponenten.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Responsive Design
|
||
|
||
---
|
||
|
||
# Mobile-First mit Media Queries
|
||
|
||
```css
|
||
/* Default: Mobile */
|
||
.sidebar { display: none; }
|
||
.content { padding: 1rem; }
|
||
|
||
/* Tablet aufwärts */
|
||
@media (min-width: 768px) {
|
||
.sidebar { display: block; width: 240px; }
|
||
.content { padding: 2rem; }
|
||
}
|
||
|
||
/* Desktop */
|
||
@media (min-width: 1280px) {
|
||
.sidebar { width: 320px; }
|
||
}
|
||
```
|
||
|
||
**Mobile-First**: kleinste Größe als Default, dann größer.
|
||
|
||
---
|
||
|
||
# Viewport-Meta
|
||
|
||
```html
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
```
|
||
|
||
**Pflicht** im `<head>`. Ohne dieses Meta zoomt iPhone & Android alles auf 980 px Breite.
|
||
|
||
---
|
||
|
||
# Fluid Sizing — ohne Media Queries
|
||
|
||
```css
|
||
/* Font: skaliert zwischen 14px und 22px */
|
||
h1 { font-size: clamp(1.4rem, 2.5vw + 1rem, 2.5rem); }
|
||
|
||
/* Padding: skaliert mit Viewport */
|
||
.card { padding: clamp(1rem, 3vw, 3rem); }
|
||
|
||
/* Breite: nie mehr als 70 Zeichen für Lesetext */
|
||
.prose { max-width: 70ch; margin: auto; }
|
||
```
|
||
|
||
`clamp(min, ideal, max)` ist modernes Allzweck-Werkzeug.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Animationen
|
||
|
||
---
|
||
|
||
# Transitions — sanfter Wechsel
|
||
|
||
```css
|
||
button {
|
||
background: hotpink;
|
||
transition: background 0.2s ease, transform 0.15s;
|
||
}
|
||
|
||
button:hover {
|
||
background: deeppink;
|
||
transform: translateY(-2px);
|
||
}
|
||
```
|
||
|
||
**Was animiert performant:** `transform`, `opacity`. **Was nicht:** `width`, `height`, `top/left`.
|
||
|
||
---
|
||
|
||
# Keyframe-Animationen
|
||
|
||
```css
|
||
@keyframes pulse {
|
||
0% { transform: scale(1); opacity: 1; }
|
||
50% { transform: scale(1.1); opacity: 0.7; }
|
||
100% { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
.notification {
|
||
animation: pulse 1.5s ease infinite;
|
||
}
|
||
```
|
||
|
||
`animation: <name> <duration> <timing> <iteration>`.
|
||
|
||
---
|
||
|
||
# Reduced Motion — Pflicht
|
||
|
||
Nicht alle wollen Animationen. (Vestibuläre Probleme, ADHS, Migräne.)
|
||
|
||
```css
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*, *::before, *::after {
|
||
animation-duration: 0.01ms !important;
|
||
transition-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Standard-Bestandteil** jeder Seite — ein User-Setting respektieren.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# CSS-Variablen + Modern
|
||
|
||
---
|
||
|
||
# Custom Properties (CSS Variables)
|
||
|
||
```css
|
||
:root {
|
||
--primary: hotpink;
|
||
--gap: 1rem;
|
||
--radius: 0.75rem;
|
||
}
|
||
|
||
button {
|
||
background: var(--primary);
|
||
padding: var(--gap);
|
||
border-radius: var(--radius);
|
||
}
|
||
|
||
/* Dark Mode: nur Variablen umsetzen */
|
||
@media (prefers-color-scheme: dark) {
|
||
:root { --primary: deeppink; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS Functions
|
||
|
||
```css
|
||
.box {
|
||
width: calc(100% - 2rem); /* Mathe */
|
||
font-size: clamp(1rem, 2vw, 2rem); /* min, ideal, max */
|
||
background: linear-gradient(45deg, hotpink, deeppink);
|
||
color: oklch(0.7 0.2 30); /* moderner Farbraum */
|
||
filter: blur(2px) brightness(1.2); /* Bildeffekte */
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS-Frameworks
|
||
|
||
| Framework | Stil |
|
||
|-----------|------|
|
||
| **Tailwind CSS** | Utility-Klassen direkt im HTML |
|
||
| **Bootstrap** | Komponenten-Bibliothek (klassisch) |
|
||
| **Pico** | klassen-frei, semantisches HTML reicht |
|
||
| **Open Props** | CSS-Variablen-Set |
|
||
|
||
```html
|
||
<!-- Tailwind: alle Styles als Klassen -->
|
||
<button class="bg-pink-500 hover:bg-pink-600 px-4 py-2 rounded-md">
|
||
Klick
|
||
</button>
|
||
```
|
||
|
||
---
|
||
|
||
# Tailwind — Argumente dafür/dagegen
|
||
|
||
**Pro:**
|
||
- Schnell, weniger Context-Switch (HTML + CSS in einer Datei)
|
||
- Konsistent (Design-System eingebaut)
|
||
- Tree-Shaking → kleines Bundle
|
||
|
||
**Contra:**
|
||
- HTML voller Klassen — schwerer zu lesen
|
||
- Lernkurve eigene
|
||
- Versions-Lock-in mit Setup
|
||
|
||
**Faustregel:** für neue Projekte ausprobieren, vorhandene Projekte nicht zwanghaft migrieren.
|
||
|
||
---
|
||
|
||
<!-- _class: aufgabe -->
|
||
|
||
# Selbstlernen — Layout-Übung
|
||
|
||
1. **Flexbox-Spiel:** [flexboxfroggy.com](https://flexboxfroggy.com) — alle 24 Level durch.
|
||
2. **Grid-Spiel:** [cssgridgarden.com](https://cssgridgarden.com) — 28 Level durch.
|
||
3. **Eigene Karten-Galerie:** `repeat(auto-fit, minmax(...))` Pattern anwenden.
|
||
|
||
**Bonus:** Dark-Mode mit CSS-Variablen + `prefers-color-scheme`.
|
||
|
||
---
|
||
|
||
# Zusammenfassung
|
||
|
||
**Heute gelernt:**
|
||
- Box-Model + `box-sizing: border-box` global
|
||
- **Flexbox** für 1D-Layouts (Navigation, Karten-Reihen)
|
||
- **Grid** für 2D-Layouts (Seiten-Layout, Galerien)
|
||
- Mobile-First Media Queries + `clamp()` für fluid
|
||
- Transitions + Keyframes — `prefers-reduced-motion` respektieren
|
||
- CSS-Variablen für Theming
|
||
- Tailwind & Co. als moderner Framework-Stil
|
||
|
||
**Nächste Stunde:** Node.js — JavaScript außerhalb des Browsers.
|