444 lines
7.8 KiB
Markdown
444 lines
7.8 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: #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;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# CSS Specificity
|
||
|
||
Welcher Selektor gewinnt? **Punktezählung:**
|
||
|
||
```
|
||
ID – 0,1,0,0
|
||
Class / Attr / Pseudo-Class – 0,0,1,0
|
||
Tag / Pseudo-Element – 0,0,0,1
|
||
Inline Style – 1,0,0,0
|
||
!important – setzt alles außer Kraft
|
||
```
|
||
|
||
```css
|
||
#main .btn:hover /* 0,1,2,0 */
|
||
.btn.primary /* 0,0,2,0 */
|
||
button[type="submit"] /* 0,0,1,1 */
|
||
```
|
||
|
||
→ Faustregel: möglichst flach. `!important` vermeiden.
|
||
|
||
---
|
||
|
||
# CSS Frameworks
|
||
|
||
| Framework | Stil |
|
||
|-----------|------|
|
||
| **Bootstrap** | Komponenten + Grid, opinionated |
|
||
| **Tailwind CSS** | Utility-First, atomar |
|
||
| **Bulma** | klassisch, ohne JS |
|
||
| **shadcn/ui** | kopierbare Komponenten (Tailwind-basiert) |
|
||
| **Flowbite** | Tailwind-Komponentenbib. |
|
||
|
||
---
|
||
|
||
# Tailwind CSS – Beispiel
|
||
|
||
```html
|
||
<button class="bg-blue-600 hover:bg-blue-700
|
||
text-white font-semibold
|
||
px-4 py-2 rounded-lg
|
||
transition shadow-sm">
|
||
Klick mich
|
||
</button>
|
||
```
|
||
|
||
- Keine eigenen CSS-Klassen mehr
|
||
- JIT-Compiler erzeugt nur genutzte Klassen
|
||
- Design-System via `tailwind.config.js`
|
||
|
||
---
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# No JavaScript, No Cry
|
||
|
||
## Native HTML statt JS-Komponenten
|
||
|
||
---
|
||
|
||
# Native HTML – Was früher JS brauchte
|
||
|
||
| Komponente | Native Alternative |
|
||
|------------|--------------------|
|
||
| Accordion / Collapsible | `<details>` + `<summary>` |
|
||
| Modal / Dialog | `<dialog>` |
|
||
| Date Picker | `<input type="date">` |
|
||
| Time Picker | `<input type="time">` |
|
||
| Color Picker | `<input type="color">` |
|
||
| Range Slider | `<input type="range">` |
|
||
| Progress Bar | `<progress>` |
|
||
| Auto-Complete | `<datalist>` |
|
||
|
||
→ Baseline Widely Available (Chrome, Firefox, Safari, Edge).
|
||
|
||
---
|
||
|
||
# `<details>` + `<summary>`
|
||
|
||
```html
|
||
<details>
|
||
<summary>Mehr Infos</summary>
|
||
<p>Hier steht der ausklappbare Inhalt.</p>
|
||
</details>
|
||
```
|
||
|
||
- Keine Zeile JS
|
||
- Tastatur-zugänglich
|
||
- Animierbar mit `::details-content` (CSS only)
|
||
|
||
---
|
||
|
||
# `<dialog>` – Native Modal
|
||
|
||
```html
|
||
<dialog id="myDialog">
|
||
<form method="dialog">
|
||
<p>Wirklich löschen?</p>
|
||
<button value="cancel">Abbrechen</button>
|
||
<button value="ok">OK</button>
|
||
</form>
|
||
</dialog>
|
||
|
||
<button onclick="myDialog.showModal()">Öffnen</button>
|
||
```
|
||
|
||
- `showModal()` blockt Hintergrund + ESC schließt
|
||
- Backdrop via `dialog::backdrop`
|
||
- Form mit `method="dialog"` schließt + liefert `returnValue`
|
||
|
||
---
|
||
|
||
# `<input type="date">` & Co.
|
||
|
||
```html
|
||
<input type="date" min="2026-01-01" max="2026-12-31">
|
||
<input type="time" step="60">
|
||
<input type="color" value="#005f8a">
|
||
<input type="range" min="0" max="100" step="5" value="50">
|
||
<input type="number" min="1" max="300">
|
||
```
|
||
|
||
- Plattform-native UI (Touch-Datepicker auf Mobile)
|
||
- Validierung "for free"
|
||
- Locale-Awareness vom Browser
|
||
|
||
---
|
||
|
||
# `<progress>` – Native Progress Bar
|
||
|
||
```html
|
||
<progress value="70" max="100">70%</progress>
|
||
|
||
<!-- indeterminate -->
|
||
<progress></progress>
|
||
```
|
||
|
||
```css
|
||
progress {
|
||
width: 100%;
|
||
height: 0.5rem;
|
||
}
|
||
progress::-webkit-progress-bar { background: #eee; }
|
||
progress::-webkit-progress-value { background: #005f8a; }
|
||
```
|
||
|
||
---
|
||
|
||
# Form Validation – Native
|
||
|
||
```html
|
||
<form>
|
||
<input type="email" required
|
||
pattern=".+@dhbw\.de"
|
||
title="Bitte DHBW-Mail eingeben">
|
||
<input type="password" minlength="8" required>
|
||
<button>Login</button>
|
||
</form>
|
||
```
|
||
|
||
- `required`, `min`, `max`, `pattern`, `step`
|
||
- `:invalid` / `:valid` CSS-Pseudoklassen
|
||
- `form.checkValidity()` / `input.setCustomValidity()`
|
||
|
||
---
|
||
|
||
# Wann doch JS?
|
||
|
||
- Komplexe Interaktion über mehrere Komponenten
|
||
- State, der nicht ins DOM gehört
|
||
- Async / Server-Sync
|
||
- Animationen mit komplexer Logik
|
||
- Cross-Browser-Polyfills (selten heute)
|
||
|
||
→ **Default: HTML/CSS. JS nur wenn nötig.**
|
||
|
||
---
|
||
|
||
# Fragen?
|
||
|
||
1. Was bedeutet `div > p` vs `div p`?
|
||
2. Wann nutzt du `nth-child(even)`?
|
||
3. Warum `--variable` statt direkt `#fff`? |