Speaker notes für den Vortrag Things you didn’t know you could do in CSS.
App: http://127.0.0.1:8000 · Leitdokument: diese Datei · Code: resources/css/demos/*
Empfohlene Länge: ~25–35 Minuten (+ Q&A)
Browser auf der Bühne: Chromium (Anchor Positioning + light-dark() am zuverlässigsten)
Setup vorher: npm run build && php artisan serve · kein parallel laufendes npm run dev mit kaputtem HMR · Editor mit resources/css/demos/ geöffnet
Damit niemand geblufft wird:
| Demo | Before unter der Haube | After unter der Haube |
|---|---|---|
:has() Checkbox |
JS setzt .is-selected |
nur :has(input:checked) — kein JS |
| Filter | JS-Loop + .dimmed |
Radios + :has() + :not() |
| Container Queries | @media Viewport |
container-type + @container |
| Anchor Tooltip | position:relative Wrapper + Offsets |
anchor-name / anchor() |
| Popover | JS toggle + click-outside | popovertarget + [popover] + Anchor |
| clamp | gestufte @media font-sizes |
clamp() / min() |
| light-dark | hardcoded Samples + live @media (prefers-color-scheme) |
echte light-dark(); Toggle setzt nur color-scheme |
| Finale | — | :has Kerzen/Konfetti, Popover, light-dark, clamp, @container |
Build erhält natives light-dark() (LightningCSS-Polyfill ist in vite.config.js ausgeschaltet).
| Thema | CSS (öffnen!) | Markup | Before-JS |
|---|---|---|---|
:has() Checkbox |
resources/css/demos/has-checkbox.css |
resources/views/demos/has-checkbox.blade.php |
before.js → initHasCheckbox |
:has() Filter |
resources/css/demos/has-filter.css |
…/has-filter.blade.php |
initHasFilter |
| Container Queries | resources/css/demos/container-queries.css |
…/container-queries.blade.php |
— |
| Anchor Tooltip | resources/css/demos/anchor-tooltip.css |
…/anchor-tooltip.blade.php |
— |
| Popover + Anchor | resources/css/demos/popover-menu.css |
…/popover-menu.blade.php |
initPopoverJs |
clamp() |
resources/css/demos/clamp.css |
…/clamp.blade.php |
— |
light-dark() |
resources/css/demos/light-dark.css |
…/light-dark.blade.php |
Toggle only |
| Finale | resources/css/demos/finale.css |
resources/views/finale.blade.php |
Popover schließen |
Jeder Demo-CSS-File startet mit einem Header: Talking Point, Key Rule, Pfade.
resources/css/app.css ist nur der Import-Index — für den Vortrag die Demo-Datei öffnen.
Zeigen: Startseite / erster Tab, Layout Before (rosa) | After (grün).
Sagen:
- Wir lösen Alltagsprobleme, für die man früher JS (oder hässliches CSS) gebraucht hat.
- Links = alter Weg, rechts = modernes CSS. Live-Previews sind klickbar.
- Am Ende eine Torte, in der alles zusammenkommt.
Überleitung: „Erstes Feature: Eltern über Kind-Zustand stylen.“
URL: /demo/has-checkbox
Datei öffnen: has-checkbox.css → Regel .select-card:has(input:checked)
Zeigen:
- Links Checkbox klicken → Highlight (JS setzt
.is-selected). - Rechts Checkbox klicken → gleiches Ergebnis, kein JS.
- Kurz den Code-Block und die CSS-Datei zeigen.
Sagen:
- Früher: Event-Listener,
classList.toggle, Parent von Hand finden. :has()erlaubt Selektoren der Form „Element, das X enthält“.- Parent war lange unerreichbar — das ändert sich.
Fallen / Fragen erwarten: Spezifität, Performance bei sehr tiefen Bäumen (praktisch meist unkritisch).
URL: /demo/has-filter
Datei: has-filter.css
Zeigen:
- Links: Filter-Buttons, Items dimmen via JS-Loop.
- Rechts: Radios + Labels; CSS filtert mit
:has(#filter-css:checked) … :not([data-tag="css"]).
Sagen:
- Typisches UI: Kategorien ein-/ausblenden.
- Statt Schleife: eine Regel pro Filterwert.
- Radios als State-Machine — CSS liest den checked-Zustand.
Überleitung: „Nicht alles ist Kind-Zustand. Manchmal ist die Frage: wie breit ist mein Parent?“
URL: /demo/container-queries
Datei: container-queries.css
Zeigen (wichtig — live resizen!):
- Links: Grid hängt am Viewport — Fenstergröße ändern.
- Rechts: gestrichelten Kasten an der Ecke ziehen → Cards brechen um bei ~400px Container-Breite.
Sagen:
@media= Viewport. Komponenten sitzen aber oft in Sidebars, Modals, Split-Views.container-type: inline-size+@container= Component-Responsive.- Das ist der mentale Shift von „Seite“ zu „Baustein“.
URL: /demo/anchor-tooltip
Datei: anchor-tooltip.css
Zeigen: Hover links (Wrapper + Offsets) und rechts (anchor-name / position-anchor / anchor()).
Sagen:
- Klassisch:
position: relativeWrapper,bottom: calc(100% + 8px),translateX(-50%)— bricht leicht. - Neu: Button bekommt einen Namen, Tooltip dockt deklarativ an.
- Bonus: Hover-Sichtbarkeit wieder mit
:has().
Hinweis auf der Folie/Seite: Chromium zuerst; Fallback ist eingebaut.
URL: /demo/popover-menu
Datei: popover-menu.css + Markup im Blade
Zeigen:
- Links: Actions → Menü; Klick außerhalb schließt (JS).
- Rechts:
popovertarget/[popover]— Open, Close, Light-Dismiss vom Browser; Platzierung per Anchor.
Sagen:
- Menüs = State + Outside-Click + Fokus — viel Boilerplate.
- Popover API nimmt Open/Close/Dismiss ab.
- Anchor Positioning ersetzt absolute Offsets.
- Zusammen: Menü ohne eigenes Mini-Framework.
Code zeigen: die zwei HTML-Zeilen popovertarget + popover.
URL: /demo/clamp
Datei: clamp.css
Zeigen: Fenster langsam resizen — links springt die Schrift, rechts skaliert flüssig.
Sagen:
- Vier Media Queries für eine
font-sizesind ein Smell. clamp(min, preferred, max)= Fluid Typography in einer Zeile.- Gleich für Spacing:
min(),gap: clamp(…).
Formel kurz: Preferred oft Xvw + Yrem — Floor und Cap halten Lesbarkeit.
URL: /demo/light-dark
Datei: light-dark.css
Zeigen:
- Links: duplizierte Properties im Dark-
@media. - Rechts: Light/Dark-Toggle → eine Card, eine Deklaration pro Property.
Sagen:
- Dark Mode = früher Copy-Paste jeder Property.
light-dark(#fff, #1a1a2e)+color-scheme.- Weniger Drift zwischen Themes.
Ehrlich bleiben: Toggle setzt color-scheme / data-scheme am Preview-Root (Mini-JS nur für die Demo). Produktiv oft OS-Preference oder ein Theme-Toggle am :root.
URL: /finale
Datei: finale.css (+ Header im File lesen)
Zeigen in dieser Reihenfolge:
- Headline „Happy Birthday“ →
clamp()(Fensterbreite). - Day / Night → Raumfarben (
light-dark()+ Scheme-Toggle). - Actions → Blow out candles → Flammen aus + Konfetti (
:has(#candle-blown:checked)). - Relight → wieder anblasen (Animation startet erneut).
- Optional: Fenster schmaler →
@containerLayout (Torte / Chips). - Feature-Chips rechts ablaufen: „Alles, was wir gesehen haben, steckt hier drin.“
Sagen:
- Kein Backend, kaum JS — State über Radios + Popover.
- Konfetti und Flammen: rein CSS, getriggert durch
:has(). - Pointe: moderne CSS-Features ersetzen ganze Klassen von UI-Glue-Code — und machen Spaß.
Schlusszeile: „Wenn ihr das nächste Mal zum addEventListener greift — kurz fragen, ob’s der Browser schon kann.“
- Browser-Support-Tabelle (Anchor,
light-dark,:has—:hasist heute breit da). - Accessibility: native Controls (Checkbox, Radio, Popover) schlagen Custom-Divs.
- Progressive Enhancement: Fallbacks in den Anchor-Dateien (
@supports not). - Wo JS bleibt: echte Business-Logik, Daten, komplexe Gesten — nicht „Parent highlighten“.
- Chromium-Fenster, Zoom 110–125 % falls Beamer
-
php artisan serveläuft; Tabs einmal durchgeklickt - Editor-Tabs vorbereitet: die 8 Dateien unter
resources/css/demos/ - Finale: Blow-out einmal geübt (Konfetti sichtbar?)
- Dieses Dokument auf Zweitbildschirm oder gedruckt
| Block | Min |
|---|---|
| Intro | 2 |
:has ×2 |
7 |
| Container | 4 |
| Anchor + Popover | 7 |
| clamp + light-dark | 6 |
| Finale | 6 |
| Puffer / Q&A | Rest |