

Responsivität, Dark Mode und Animationen
Websites müssen in allen erdenklichen Formaten funktionieren – und es wäre schön, wenn sich auf ihnen auch etwas bewegen könnte, oder?
Wiederholung
In der letzten Vorlesung haben wir uns mit CSS-Eigenschaften, der Einbettung von Schriften, dem Box-Modell sowie mit den Layoutsystemen Flexbox und Grid beschäftigt.
Wie ändere ich die Text- und Hintergrundfarbe eines Elements mit CSS?
Die Hintergrundfarbe eines Elements lässt sich mit der CSS-Eigenschaft background-color verändern, die Textfarbe mit color. Bei beiden Eigenschaften könnt ihr wahlweise einen Hex-Code, eine benannte Farbe, oder eine Farbfunktion hinterlegen, z.B. color: rgb(255 255 255 / 0.5) für ein Weiß mit 50% Deckkraft.
Was macht @font-face?
Die @font-face At-Regel definiert eine eigene Schriftart zur Verwendung auf der Website. Die font-family-Eigenschaft legt den Namen der Schriftart fest, die src-Eigenschaft die Quelle, die wahlweise eine lokale oder eine Server-URL sein kann.
Aus Performance- und Datenschutzgründen, solltet ihr immer lokale URLs in src verwenden und die benötigten Font-Dateien (meistens im Format WOFF2) direkt als Teil eurer Website ausliefern. Schaut im letzten Skript nach, wie ihr euch hierfür das Leben leichter machen könnt.
Wie erstelle ich einen Farbverlauf in CSS?
Farbverläufe in CSS zählen als Bilder, d.h., ihr setzt sie mit der Eigenschaft background-image. Es gibt verschiedene Funktionen, um verschiedene Verlaufsarten zu verwenden. Mit linear-gradient() könnt ihr lineare Farbverläufe erstellen, mit radial-gradient() Radiale und mit conic-gradient() Konische.
Die Syntax kann schnell komplex werden. Deshalb könnt ihr auch die Verläufe in Figma erstellen und über Rechtsklick → Copy / Paste as → Copy as code → CSS kopieren.
Aus welchen vier Boxen besteht das Box-Modell?
Das Box-Modell besteht aus (von innen nach außen):
Content-Box für den Inhalt eines Elements
Padding-Box für den Abstand zwischen dem Inhalt eines Elements und seinem Rand
Border-Box für den Rahmen um ein Element
Margin-Box für den Abstand vom Rand eines Elements zu anderen Elementen
Welche drei Möglichkeiten habe ich, um ein Element auf meiner Website auszublenden?
Ein Element kann auf drei verschiedene Weisen ausgeblendet werden:
Mit
display: none;– hierbei wird das Element komplett aus dem Dokument entfernt, als wäre es nie Teil davon gewesen. Es ist aber noch im Code und kann somit via JavaScript angesprochen werden.Über
opacity– diese Eigenschaft bestimmt die Deckkraft eines Elements. Der Wert1steht für volle Deckkraft,0für komplette Transparenz. Opacity kann sehr performant animiert werden!Mit
visibility: hidden;– hierbei wird das Element unsichtbar (wie mitopacity: 0;), aber auch semantisch als versteckt deklariert und ist somit nicht mehr über die Tastatur oder einen Bildschirmleser erreichbar.
Nur falls ein Element mit display: none; entfernt wird, gibt der Browser den Platz, den das Element einnehmen würde, auch wieder frei. Bei den anderen beiden Methoden entstehen „Lücken“ in der Darstellung an den Orten, an denen das Element sitzt.
Was sind die drei wichtigsten Eigenschaften für ein CSS-Grid?
Grid ist eine sehr flexible Möglichkeit, Layouts in CSS umzusetzen. Deshalb gibt es auch sehr viele verschiedene Eigenschaften dafür. Um mit CSS-Grid anzufangen, reichen aber drei Eigenschaften völlig aus:
Mit
display: grid;aktiviert ihr das Grid-Layoutsystem für ein ElementMit
grid-template-columnskönnt ihr die Anzahl der Spalten und ihre Breite bestimmen.1frsteht hierbei für einen Bruchteil des verbleibenden Platzes, so könnt ihr z.B. mitgrid-template-columns: repeat(12, 1fr);ein Raster aus 12 gleich großen Spalten definieren. Mitgrid-template-columns: 200px 1fr 2fr;hättet ihr hingegen ein Raster, bei dem die erste Spalte 200px groß ist, die zweite einen Bruchteil, und die dritte 2 Bruchteile des verbleibenden Platzes. D.h., die dritte Spalte wäre doppelt so breit wie die zweite.Mit
gapbestimmt ihr den Abstand zwischen Spalten
Falls ihr Kindelemente des Elements mit display: grid; in bestimmte Spalten verschieben, oder über mehrere Spalten spannen lassen möchtet, könnt ihr das mit den Eigenschaften grid-column-start und grid-column-end tun.
Responsive Design
Ihr habt es schon gehört, als es um die Erstellung eurer Screendesigns in einem Design-Tool wie Figma ging: Eine Website muss auf jeder erdenklichen Bildschirmgröße bzw. in jedem Viewport funktionieren, sei es das kleinste Telefon oder der größte Fernseher. Es gibt keinen „idealen“ Viewport – Nutzerinnen und Nutzer haben eure Website vielleicht auf ihrem großen 27-Zoll Bildschirm in einem Browserfenster geöffnet, das nur 320 mal 320 Pixel groß ist. Ihr könnt also nicht kontrollieren, wo eure Website dargestellt wird – nur, wie sie dargestellt wird. Und wie wir gelernt haben, macht ihr die Darstellung in CSS.
CSS bietet euch neben den relativen Einheiten, die wir beim letzten Mal kennengelernt haben, auch noch einige weitere Werkzeuge, um eure Website responsiv zu machen. Mit diesen werden wir uns heute befassen.
Als Responsivität bezeichnet man im Web die Fähigkeit einer Website, sich an verschiedene Geräte anzupassen. Das geht von den offensichtlichen Layoutanpassungen zwischen einem Quer- und einem Hochformat bis hin zu spezifischen UX-Anpassungen, wie der Verlagerung von wichtigen Interaktionsflächen in Daumenreichweite auf Smartphones.
Progressive Enhancement
Hand in Hand mit responsivem Design geht das Konzept des „Progressive Enhancement“. Hierbei wird sichergestellt, dass die Website auch dann noch benutzbar bleibt, wenn neue Features und Funktionen im Browser, oder JavaScript wegen eines fehlerhaften Skripts oder durch Nutzerdeaktivierung nicht verfügbar sind.
Ein einfaches Progressive Enhancement ist beispielsweise die Angabe von Fallback-Schriften, die wir beim letzten Mal kennengelernt haben: font-family: "Atkinson Hyperlegible Variable", sans-serif;. Falls die Schriftart „Atkinson Hyperlegible Variable“ nicht geladen werden kann, werden die Texte auf der Website dennoch in einer serifenlosen Schriftart angezeigt.
Ein anderes Beispiel wäre eine Navigationsleiste mit weichgezeichnetem Hintergrund. Nicht alle Browser unterstützen die CSS-Eigenschaften für das Weichzeichnen des Hintergrunds. Deshalb muss sichergestellt werden, dass der Text innerhalb der Navigationsleiste auch dann noch lesbar ist, wenn der Hintergrund nicht weichgezeichnet werden kann. Das kann beispielsweise dadurch erreicht werden, dass man zusätzlich zum Effekt auch eine halbtransparente Hintergrundfarbe auf das Element setzt.
<header>
Ich bin ein Header mit Blur-Effekt
</header>
<header class="coloured">
Ich bin ein Header mit Blur-Effekt und Farbüberlagerung
</header>
<main>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Recusandae nemo vel quisquam obcaecati, totam facere atque alias expedita debitis reiciendis ipsam, architecto accusamus molestiae enim, ex officia eos tempore amet!
</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero laboriosam quasi aut tempora temporibus delectus eveniet saepe corporis, aliquam, doloremque deleniti recusandae officia iusto, natus excepturi dicta magni inventore. Esse.</p>
<p>Rerum dicta facere nulla sunt non ullam iure libero id quam mollitia suscipit, nisi unde corporis beatae, obcaecati at aliquid reprehenderit eius veniam quae? Illo reiciendis aliquam cupiditate magnam dolore?</p>
<p>Sapiente blanditiis explicabo nostrum nobis inventore ea eveniet assumenda unde? Iste necessitatibus suscipit quibusdam totam laudantium reiciendis consequuntur sit doloribus in. Ab commodi veritatis repellendus numquam ex natus temporibus suscipit.</p>
<p>Ullam harum corrupti vel, quod quidem ipsum architecto optio deserunt eum sit quas quis, pariatur officiis cupiditate delectus reiciendis neque excepturi! Unde itaque iusto necessitatibus nesciunt sapiente fugit temporibus repellat.</p>
<p>Voluptatem vitae adipisci quas corporis corrupti magni quae saepe dignissimos suscipit ipsam omnis error laudantium similique ut rerum nulla facere, quibusdam fuga odio minus non cumque at aliquam. Non, esse.</p>
</main>
body {
font-family: sans-serif;
line-height: 1.75;
}
main {
max-width: 72ch;
margin-inline: auto;
}
header {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 2rem;
backdrop-filter: blur(0.5rem);
&.coloured {
top: 4.75rem;
background-color: rgb(from white r g b / 0.8);
}
}
Media-Queries
Um unterschiedliche CSS-Regeln in bestimmten Situationen anzuwenden, könnt ihr Media Queries verwenden. Das sind spezielle Abfragen, die gerätespezifische Eigenschaften wie Bildschirmbreite, Verfügbarkeit von Hover, oder auch systemweiten Dark Mode erkennen können.
Verwenden könnt ihr diese in <link> und <meta>-Elementen, sowie als Teil des speziellen @media-Selektors in CSS. Sie bestehen immer aus einem optionalen Medientyp (meistens screen, da wir ja die Website auf einem Bildschirm anzeigen) und einem oder mehreren Media-Feature-Ausdrücken, die mit logischen Operatoren verbunden werden können.
Mehr Informationen im Detail könnt ihr bei CSS-Tricks oder MDN erhalten. Wir werden uns im Kurs nur mit den häufigsten Aspekten befassen.
Media Queries in CSS
In CSS habt ihr zwei verschiedene Möglichkeiten, den @media-Selektor zu verwenden. Entweder ihr verwendet ihn klassisch, um eine Gruppe von Regeln zu aktivieren, oder verschachtelt, um einzelne Regeln zu verändern. Beide Methoden sind legitim und schließen sich nicht gegenseitig aus.
In folgendem Beispiel wird über eine klassische Media-Query eingestellt, dass die Elemente in der Liste ab einer Breite der Website von mindestens 40rem (640px) nebeneinander statt untereinander dargestellt werden. Die Helfer-Klassen .mobile-only, .desktop-only und .dark-only hingegen verwenden verschachtelte Media-Queries, um Elemente nur auf schmalen, breiten oder Geräten im Dark-Mode anzuzeigen.
<p class="dark-only">Nur sichtbar, wenn im Dark-Mode</p>
<ul>
<li>
<p class="mobile-only">Untereinander</p>
<p class="desktop-only">Nebeneinander</p>
</li>
<li>
<p class="mobile-only">Untereinander</p>
<p class="desktop-only">Nebeneinander</p>
</li>
<li>
<p class="mobile-only">Untereinander</p>
<p class="desktop-only">Nebeneinander</p>
</li>
<li>
<p class="mobile-only">Untereinander</p>
<p class="desktop-only">Nebeneinander</p>
</li>
</ul>
body {
font-family: system-ui, sans-serif;
}
p.dark-only {
background-color: #111;
color: #fefefe;
padding: 2rem;
text-align: center;
}
ul {
padding: 1rem;
border: 0.0625rem solid grey;
list-style: none;
display: flex;
flex-direction: column;
gap: 1rem;
li {
padding: 1rem;
background-color: salmon;
flex-grow: 1;
p {
margin: 0;
}
}
}
/* Klassische Media-Query */
@media screen and (min-width: 40rem) {
ul {
flex-direction: row;
}
}
/* Verschachtelte Media-Queries */
.dark-only {
/* Standard: unsichtbar */
display: none;
/* Wenn im Dark-Mode: sichtbar */
@media (prefers-color-scheme: dark) {
display: block;
}
}
.mobile-only {
display: none;
/* Wenn Website-Breite kleiner 640px: sichtbar */
@media (width < 40rem) {
display: block;
}
}
.desktop-only {
display: none;
/* Wenn Website-Breite größer 640px: sichtbar */
@media (width > 40rem) {
display: block;
}
}
Media Queries in HTML
Anstatt innerhalb eines CSS-Stylesheets einzelne Regeln zu aktivieren oder zu deaktivieren, könnt ihr auch ganze Stylesheets direkt im HTML nur unter bestimmten Umständen laden. Dazu fügt ihr eurem <link>-Element ein media-Attribut hinzu. So könnt ihr z.B. die Datei mobile.css nur dann laden und aktivieren, wenn die Seite auf einem Bildschirm angezeigt wird und der Viewport schmaler als 640px ist:
<link
rel="stylesheet"
href="/styles/mobile.css"
media="screen and (max-width: 640px)"
>
Achtet hierbei auf die Reihenfolge, in der ihr die Stylesheets einbindet. Eure allgemeinen Regeln sollten immer geladen werden, bevor ihr sie in spezifischen Umständen wie verschiedenen Bildschirmgrößen oder z.B. im Dark-Mode überschreibt. Fügt eure Stylesheets mit media-Attribut also immer nach den allgemeinen Stylesheets ein.
Das bedeutet auch, dass ihr in euren Alternativen Stylesheets nicht alle Regeln wiederholen müsst. Ihr überschreibt in ihnen nur die Eigenschaften, du sich unterscheiden sollen.
Innerhalb eines media-Attributs könnt ihr jede Media-Query verwenden, die ihr auch in @media in CSS benutzen würdet.
Vendor-Prefixe
CSS entwickelt sich immer weiter und bietet euch immer mehr Möglichkeiten, bestimmte Effekte zu erzielen und eure Seiten noch responsiver zu gestalten. Leider funktionieren aber nicht alle Eigenschaften in allen Browsern bzw. Browser-Versionen und es kann dauern, bis eine neue Funktion standardisiert ist.
In der Vergangenheit haben Browser-Hersteller deshalb neue Features oftmals hinter besonderen CSS-Eigenschaften versteckt, damit Nutzer diese früher testen können. Diese besonderen Eigenschaften haben immer den Namen des Browser-Herstellers vorgestellt, deshalb nennt man sie Vendor-Prefixes.
Inzwischen gibt es nur noch zwei große Präfixe: -moz- für Firefox und -webkit- für Chrome und Safari, sowie alle Browser, die auf Chromium basieren. Das -ms-–Prefix war für Internet Explorer und frühe Versionen von Microsoft Edge und wird deshalb nicht mehr so häufig gebraucht.
Um ein gutes Progressive Enhancement zu gewährleisten, solltet ihr bei neueren CSS-Eigenschaften immer auch die Eigenschaften mit den jeweiligen Prefixen angeben. Ob ihr ein Prefix braucht oder nicht, könnt ihr auf der Website caniuse.com nachsehen.
Gebt dort die CSS-Eigenschaft ein, die ihr verwenden möchtet, z.B. backdrop-filter. Dann seht ihr eine Tabelle mit verschiedenen Browsern und Versionen. Grüne Versionen unterstützen das Feature, orange Versionen unterstützen es teilweise und rote Versionen unterstützen es nicht. Grüne Versionen, die mit einem gelben „–“ gekennzeichnet sind, erfordern ein Vendor-Prefix.

Die gute Nachricht ist, dass man heutzutage kaum noch Vendor-Prefixe braucht. Achtet beim Verwenden von Eigenschaften einfach darauf, dass sie mit „Baseline 2024“ oder älter gekennzeichnet sind, dann könnt ihr sie in den meisten Fällen ohne Prefixe nutzen.
In der Industrie kommen häufig auch Werkzeuge wie „Autoprefixer“ vor, die den CSS-Code beim Veröffentlichen der Website analysieren und automatisch Prefixe einfügen.
Pseudo-Elemente
Ihr habt bereits Element-Selektoren kennengelernt, die es euch erlauben, die Elemente, die ihr in eurem HTML-Dokument verwendet, direkt in CSS auszuwählen. Zusätzlich zu diesen Selektoren gibt es außerdem auch noch sogenannte Pseudo-Element-Selektoren, die es euch erlauben, bestimmte Teile von Elementen auszuwählen und anzupassen.
Diese „Elemente“ existieren so nicht direkt in eurem HTML-Code, daher der Begriff „Pseudo“ – aber sie sind für bestimmte Effekte unerlässlich. Ob in einem Selektor ein Pseudo-Element ausgewählt wird, erkennt ihr daran, dass sich darin zwei aufeinanderfolgende Doppelpunkte befinden: p::first-letter wählt zum Beispiel den ersten Buchstaben in allen <p>-Elementen aus.
Wichtig: Ihr könnt immer nur ein Pseudo-Element pro CSS-Selektor auswählen, ein Selektor wie p::first-line::first-letter wäre also ungültig.
<p class="intro">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nihil mollitia porro minus ea dolores odit aliquam eius, quasi totam aut, ipsum nesciunt repellat recusandae sit rem veritatis sequi saepe qui?
</p>
<blockquote>Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque illum perferendis excepturi ratione a aperiam veniam voluptatibus, illo sunt aliquam unde officia sequi sapiente pariatur, id qui iure expedita ab.</blockquote>
<p class="important">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Repudiandae porro, reiciendis aliquam illo eum odio cupiditate ea blanditiis rerum quam qui nisi vel exercitationem commodi error doloribus ut recusandae fugiat?
</p>
/* Erstes Zeichen im Element mit der Klasse "intro" */
.intro::first-letter {
font-size: 2em;
font-weight: 600;
color: salmon;
}
/* Vor dem Inhalt des <blockquote>-Elements */
blockquote::before {
content: "„";
}
blockquote {
font-style: italic;
/*
Pseudo-Element-Selektoren können natürlich auch mit
Nesting verwendet werden. So lassen sich auch schneller
mehrere Pseudo-Elemente mit dem selben Basis-Selketor
auswählen
*/
&::before, &::after {
color: rgb(from currentColor r g b / 0.5);
font-family: serif;
font-weight: bold;
}
/* Nach dem Inhalt des <blockquote>-Elements */
&::after {
content: "“";
}
}
/* Erste Zeile im Element mit der Klasse "important" */
.important::first-line {
text-transform: uppercase;
}
body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin-inline: auto;
margin-block: 0;
padding-block: 3rem;
}
Im obigen Beispiel seht ihr zwei der am häufigsten verwendeten Pseudo-Elemente: ::before und ::after. Mit diesen könnt ihr Inhalte vor bzw. nach dem Inhalt eines Elements einfügen, wie z.B. die Anführungszeichen in der Blockquote im Beispiel.
Häufig werden diese beiden Elemente für spezifische Effekte beim Überfahren mit der Maus verwendet. Aber wie erkennt man, ob ein Element mit der Maus überfahren wird?
Pseudo-Klassen
Im Gegensatz zu Pseudo-Elementen, wählt ihr mit Pseudo-Klassen Elemente in einem bestimmten Zustand, z.B. „mit der Maus überfahren“, aus. Ihr könnt sie euch wie „virtuelle“ Klassen vorstellen, die einem Element zugewiesen werden, wenn es sich in einem spezifischen Zustand befindet.
Eine Pseudo-Klasse erkennt ihr daran, dass sie mit einem einfachen Doppelpunkt an einen Selektor angeschlossen ist, z.B. a:hover.
Häufige Pseudo-Klassen sind:
:hover: mit der Maus überfahren:focus-visible: mit der Tab-Taste fokussiert:first-child: ist das erste Kind-Element:last-child: ist das letzte Kind-Element
Eine Liste mit allen Pseudo-Klassen findet ihr hier im MDN.
<ul>
<li>
Lorem <a href="#">ipsum dolor</a> sit amet consectetur adipisicing elit. Vel quod sequi hic ad expedita pariatur eligendi fugit porro assumenda delectus quam eveniet distinctio, nam omnis, magnam aliquid veritatis dolorum voluptatum?
</li>
<li>
<button>Click me!</button>
</li>
<li>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Eveniet temporibus commodi sunt minima. Rerum, dicta quaerat ex dolores libero quos reprehenderit iure qui vel perspiciatis quasi distinctio iste maiores odio.
</li>
</ul>
ul {
list-style: none;
margin: 0;
padding: 0;
li {
border: 1px solid salmon;
padding: 1rem;
/* Nicht das erste Kindelement */
&:not(:first-child) {
margin-top: 1rem;
}
}
}
/* Link mit der Maus überfahren */
a:hover {
color: red;
}
/* Button hat Tastaturfokus */
button:focus-visible {
scale: 1.5;
}
body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin-inline: auto;
margin-block: 0;
padding-block: 3rem;
}
Positionierung
Normalerweise wird die Position eines HTML-Elements durch den Browser automatisch bestimmt, aber in Ausnahmefällen kann es vorkommen, dass ihr etwas manuell positionieren möchtet. Die position-Eigenschaft erlaubt es uns, festzulegen, nach welchem Prinzip ein Element positioniert werden soll. Es gibt fünf Werte:
static: Standardwert, der Browser kümmert sich um allesrelative: das Element wird vom Browser positioniert, kann aber relativ zu dieser automatischen Position verschoben werdenabsolute: der Browser ignoriert das Element in seinen Positionierungsberechnungen und ihr könnt es frei positionieren. Die Positionsangabe erfolgt dabei relativ zum nächsten positionierten Elternelement, d.h. ein Element mitposition: absolute;innerhalb eines Elements mitposition: relative;wird relativ zur Position des Elternelements positioniert – siehe das Beispiel unten.fixed: wieabsolute, nur relativ zum Viewport, d.h. es scrollt nicht mitsticky: wiestatic, außer es erreicht durch Scrollen den Viewport-Rand, dann wiefixed
Die eigentliche Position des Elements könnt ihr dann mit den Eigenschaften top, left, bottom und right bestimmen. Ihr müsst nicht alle dieser Eigenschaften setzen. Wenn ihr z.B. nur bottom und right setzt, werden left und top vom Browser automatisch bestimmt (der Standardwert ist auto).
<header class="fixed">
Ich bin ein fixierter Header
</header>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Maiores corporis explicabo, nemo at officiis, hic necessitatibus, ex officia temporibus deserunt culpa. Amet, quidem eaque. Iure quaerat architecto aliquam nulla impedit.</p>
<button class="sticky">Ich bleibe oben kleben!</button>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis dolorem soluta consequatur in et id quasi accusamus eaque ab, amet, impedit sed aliquam illo suscipit asperiores ut ipsam, possimus ipsum.</p>
<section>
<div class="box relative">
Ich bin relativ positioniert!
<div class="box absolute">
Ich bin absolut positioniert!
</div>
</div>
<div class="box absolute">
Ich bin absolut positioniert!
</div>
</section>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis facere minima veniam accusamus, commodi dicta hic nobis? Beatae, tempora fuga sunt quis quisquam corporis numquam autem nemo, molestias dolores cumque!</p>
<button class="fixed">Scroll to top</button>
/*
Fix an der oberen Kante des Viewports verankert,
scrollt nicht mit
*/
header.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 1rem;
background-color: rgb(0 0 0 / 0.5);
color: white;
backdrop-filter: blur(0.5rem);
}
/*
Fix an der unteren rechten Ecke des Viewports verankert,
scrollt nicht mit
*/
button.fixed {
position: fixed;
/* top und left bleiben "auto" */
right: 1rem;
bottom: 1rem;
}
/* Bleibt 4rem von der oberen Kante des Viewports kleben */
button.sticky {
position: sticky;
top: 4rem;
}
section {
border: 1px solid lightsalmon;
}
.box {
padding: 0.5rem;
width: 10rem;
aspect-ratio: 1;
background-color: salmon;
/*
Relativ positioniert,
um jeweils 1rem nach unten und rechts verschoben,
beachtet, wie die Größe der Section sich nicht verändert!
*/
&.relative {
position: relative;
top: 1rem;
left: 1rem;
}
/*
Absolut positioniert,
um jeweils 2rem nach unten und links verschoben.
Da die lachsfarbene Box nicht innerhalb eines
positionierten Elements ist, beziehen sich die Angaben
auf den Viewport.
Da die pinke Box innerhalb eines positionierten Elements
ist, beziehen sich die Angaben auf dieses Element
*/
&.absolute {
position: absolute;
right: 2rem;
top: 2rem;
}
.box {
background-color: lightpink;
width: 50%;
}
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding-inline: 1.5rem;
padding-top: 4rem;
min-height: 200vh;
}
// Dieser Code scrollt die Seite bei Klick auf den button
// mit der Klasse "fixed" wieder nach oben
document.querySelector('button.fixed').addEventListener('click', () => {
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
});
Transformierung
Oftmals will man in der Webentwicklung die Position (oder Dimension) eines Elements als Auswirkung einer Nutzerinteraktion, z.B. beim Hovern oder bei einem Klick, verändern. Da diese Veränderung meist temporär ist und auch mehrmalig auftreten kann, wäre es nicht sinnvoll, sie über die position-Eigenschaft oder das Verändern der width- und height-Eigenschaften zu erzielen. Stattdessen gibt es die transform-Eigenschaft und ihre Verwandten, um solche Änderungen elegant und performant umzusetzen.
Wie es der Name schon vermuten lässt, transformiert die transform-Eigenschaft ein Element, sei es die Position, Dimension, Rotation – oder eine Mischung aus allen Dreien. Änderungen an der Position funktionieren ähnlich wie position: relative;, d.h. das Element wird relativ zu seiner automatisch berechneten Position verschoben, allerdings kennt die transform-Eigenschaft auch eine dritte Dimension, was es euch erlaubt, Pseudo-3D-Effekte zu erzielen.
Generell ist die transform-Eigenschaft im Gegensatz zu position für Animationen optimiert, was sie zum perfekten Werkzeug für Animationen macht. Aber Achtung: Inline-Elemente können nicht transformiert werden!
Der Wert der transform-Eigenschaft sind eine oder mehrere Transformations-Funktionen wie translate(), rotate(), scale() und einige mehr. So wird ein Element mit transform: rotate(45deg); um 45° im Uhrzeigersinn gedreht.
Alternativ könnt ihr auch die Eigenschaften translate , rotate, und scale direkt verwenden (oder sie mit transform kombinieren), um eine Transformation zu erzielen. In allen Fällen legt ihr den Ursprung der Transformation mit transform-origin fest. So könnt ihr z.B. ein Element um seinen Mittelpunkt, aber auch um seine rechte obere Ecke rotieren lassen.
Es gibt noch deutlich mehr über diese Eigenschaften zu lernen, aber für den Anfang könnt ihr hiermit schon viel erreichen. Falls ihr euch für mehr interessiert, könnt ihr bei CSS-Tricks nachlesen.
<h1>Hovert die Boxen</h1>
<section>
<div class="box translate"></div>
<div class="box rotate"></div>
<div class="box scale"></div>
<div class="box all"></div>
</section>
.box {
padding: 0.5rem;
width: 6rem;
aspect-ratio: 1;
background-color: salmon;
transition: transform 200ms ease;
&.translate {
/*
Verschiebt das Element um -2rem auf der X-
und 2rem auf der Y-Achse
Equivalent zu:
translate: -2rem 2rem;
*/
transform: translate(-2rem, 2rem);
}
&.rotate {
/*
Rotiert das Element um 45° im Uhrzeigersinn
Equivalent zu:
rotate: 45deg;
*/
transform: rotate(45deg);
}
&.scale {
/*
Skaliert das Element auf die Hälfte seiner Größe
auf der X- und Y-Achse
Equivalent zu:
scale: 0.5;
*/
transform: scale(0.5);
}
&.all {
/*
Wendet alle Transformationen auf einmal an,
die Reihenfolge der Transformationen ist von rechts nach links,
d.h. in diesem Beispiel erst Skalierung, dann Rotation,
dann Transformation
*/
transform: translate(-2rem, 2rem) rotate(45deg) scale(0.5);
}
/* So lange nicht gehovert, keine Transformation */
&:not(:hover) {
transform: none;
}
}
section {
display: flex;
gap: 1rem;
padding: 1rem;
border: 1px solid magenta;
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 1.5rem;
box-sizing: border-box;
display: grid;
place-content: center;
min-height: 100vh;
}
Z-Index
Normalerweise überlappen Elemente, die im HTML weiter unten stehen, Elemente, die weiter oben in der Datei definiert sind. Ist ein Element allerdings positioniert oder transformiert, wird es über allen nicht positionierten oder -transformierten Elementen angezeigt.
Positionierte Elemente können mit z-index-Eigenschaft bestimmen, wie sie andere positionierte Elemente überlappen. Ein höherer Z-Index bedeutet, dass das Element über einem anderen Element mit niedrigerem Z-Index (im selben Stacking-Context) angezeigt wird.
Stellt es euch am besten wie die Ebenen in Figma oder Photoshop vor: Elemente mit einem höheren Z-Index sind weiter oben im Ebenenstapel und werden deshalb vor Elementen mit niedrigerem Z-Index angezeigt.
Auch hier gilt: Lasst den Browser sein Ding machen. In 99% der Fälle müsst ihr keinen eigenen Z-Index setzen, lediglich für bestimmte Effekte, z.B. ein animiertes Bild im Hintergrund, wird die z-index-Eigenschaft benötigt.
<section>
<div class="box">Eins</div>
<div class="box">Zwei</div>
<div class="box">Drei</div>
</section>
.box {
padding: 0.5rem;
width: 6rem;
aspect-ratio: 1;
background-color: salmon;
/* Nur bei positionierten kann Z-Index gesetzt werden */
position: absolute;
&:nth-child(2) {
top: 2rem;
left: 2rem;
background-color: lightsalmon;
/* Höherer Z-Index, wird deshalb über "Drei" angezeigt */
z-index: 1;
}
&:last-child {
top: 3rem;
left: 3rem;
background-color: darksalmon;
&:hover {
/*
Bei Hover hat die letzte Box auch einen Z-Index von 1
und wird deshalb über der zweiten Box angezeigt
*/
z-index: 1;
}
}
}
section {
display: flex;
gap: 1rem;
padding: 1rem;
border: 1px solid magenta;
position: relative;
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 1.5rem;
box-sizing: border-box;
display: grid;
place-content: center;
min-height: 100vh;
}
Animationen
In CSS lassen sich zwei verschiedene Formen von Animationen definieren:
Animationen über Zeit
Animationen bei Veränderungen
Dafür benötigt ihr zwei Shorthand-Eigenschaften: animation für Animationen über Zeit und transition für Animationen bei Veränderungen.
Klassische Animationen über Zeit
Diese Art von Animationen kennt ihr vielleicht bereits. Sie sind eine Sammlung von Zuständen (auch Keyframes genannt), die über Zeit abgespielt werden. Falls ihr jemals ein Daumenkino gebastelt habt, dann könnt ihr euch die einzelnen Keyframes als die einzelnen Seiten in diesem Daumenkino vorstellen. Die Animation passiert, indem schnell zwischen den Seiten / Frames gewechselt wird.
Im Browser ist das ähnlich: Ihr definiert in der speziellen @keyframes-Regel einen Namen für eure Animation, sowie eine Reihe von Zuständen. Aber jeden einzelnen Zustand separat definieren zu müssen, reicht es, nur die Veränderungen und zu welchem Zeitpunkt innerhalb der Animation sie erreicht sein sollen, zu definieren. Der Browser berechnet die Schritte dazwischen von selbst.
Die einfachste Animation wäre @keyframes einfach { to { scale: 2; } } – sie besagt, dass das Element bis zum Ende der Animation auf seine doppelte Größe vergrößert werden soll. to ist dabei ein Alias für 100%.
Der Vorteil, die Keyframes separat zu definieren, ist, dass ihr somit die gleiche Animation auf viele verschiedene Elemente anwenden könnt, ohne euch wiederholen zu müssen.
Auf ein oder mehrere Elemente anwenden könnt ihr diese einfache Animation dann mit der animation-Eigenschaft: animation: einfach 1s linear; – damit wächst das Element innerhalb von einer Sekunde mit einem linearen Easing auf seine doppelte Größe an.
Wie bereits erwähnt, ist animation eine Shorthand-Property für Eigenschaften wie animation-name, animation-duration, animation-timing-function und viele mehr. Für den Anfang reicht es, wenn ihr euch folgendes Schema für ihren Wert merkt: Name Dauer Easing Wiederholungen.
Name und Dauer sollten selbsterklärend sein. Mit Easing ist die Kurve gemeint, über die der Wert innerhalb der Dauer verändert wird. Lineares Easing verändert ihn gleichmäßig, kann aber sehr mechanisch wirken. Oft möchtet ihr eine Easing-Funktion wie ease verwenden, um eurer Animation ein organischeres Gefühl zu geben. Auf der Website Easing Wizard könnt ihr mit verschiedenen Kurven experimentieren und euch den passenden CSS-Code dafür kopieren.
Die Anzahl der Wiederholungen bestimmt, wie oft die Animation abgespielt werden soll. Hier ist das Schlüsselwort infinite besonders interessant, denn das sorgt dafür, dass eure Animation unendlich oft abspielt und somit zu einem Loop wird.
<section>
<div class="box">Eins</div>
<div class="box">Zwei</div>
<div class="box">Drei</div>
</section>
/*
Mit @keyframes werden die einzelnen Schritte der Animation
bestimmt und die Animation benannt
*/
@keyframes wiggle {
0% {
transform: rotate(0);
}
25% {
transform: rotate(5deg);
}
50% {
background-color: lightsalmon;
scale: 1.1;
}
75% {
transform: rotate(-5deg);
}
100% {
transform: rotate(0);
}
}
.box {
padding: 0.5rem;
width: 6rem;
aspect-ratio: 1;
background-color: salmon;
animation: wiggle 1s linear infinite;
&:nth-child(2) {
animation-delay: 250ms;
}
&:last-child {
animation-delay: 500ms;
}
}
/*
Wichtig! Wenn User:innen im Browser einstellen,
dass sie keine Animationen möchten,
dann sollte eure Website das respektieren.
Das ist wichtig für die Barrierefreiheit
*/
@media (prefers-reduced-motion: reduce) {
.box {
animation: none;
}
}
section {
display: flex;
gap: 1rem;
padding: 1rem;
border: 1px solid magenta;
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 1.5rem;
box-sizing: border-box;
display: grid;
place-content: center;
min-height: 100vh;
}
Wichtig: Sobald ihr größere Animationen auf eurer Website verwendet, solltet ihr in eurem CSS eine Media-Query einfügen, die diese Animationen deaktiviert, falls Besucherinnen oder Besucher in ihrem Browser eingestellt haben, dass sie weniger Animationen sehen möchten. Manchen Menschen wird übel, wenn sich zu viel bewegt. Deshalb ist eine Beachtung von @media (prefers-reduced-motion: reduce) essenziell für die Barrierefreiheit!
Animationen bei Verändertungen
Viel häufiger als klassische Animationen kommen in CSS sogenannte Transitions vor, also Animationen bei Veränderungen von Zuständen, zum Beispiel wenn ein Element gehovert oder mit der Tastatur fokussiert wird.
Diese haben gegenüber klassischen Animationen den Vorteil, dass ihr nicht extra Keyframes definieren müsst, sind aber auch ein wenig weniger flexibel, da ihr nur einen Start- und Endwert für einzelne Eigenschaften angeben könnt.
Ihr könnt eine Transition für ein Element mit der transition-Eigenschaft aktivieren. Der Wert dieser Shorthand-Property besteht aus einer Liste von Eigenschaften und Optionen in folgendem Schema: Eigenschaft Dauer Easing Verzögerung.
Eigenschaft kann hierbei fast jede CSS-Eigenschaft sein, aber bitte beachtet, dass nur transform und opacity für Animationen optimiert sind und viele Eigenschaften wie display, border-style, etc. nicht wirklich sinnvoll animiert werden können, da ihre Werte keine Zahlen sind.
Dauer und Easing funktionieren wie bei animation, zusätzlich könnt ihr auch noch eine Verzögerung angeben, die der Browser abwartet, bevor er die Animation abspielt.
<h1>Hovert die Boxen</h1>
<section>
<div class="box">Eins</div>
<div class="box">Zwei</div>
<div class="box">Drei</div>
</section>
.box {
padding: 0.5rem;
width: 6rem;
aspect-ratio: 1;
background-color: salmon;
transition: scale 250ms ease, opacity 350ms ease 100ms;
}
/*
Wichtig! Wenn User:innen im Browser einstellen,
dass sie keine Animationen möchten,
dann sollte eure Website das respektieren.
Das ist wichtig für die Barrierefreiheit
*/
@media (prefers-reduced-motion: reduce) {
.box {
transition: none;
}
}
section {
display: flex;
gap: 1rem;
padding: 1rem;
border: 1px solid magenta;
/* Wenn die Section gehovert ist */
&:hover {
/*
Werden alle Boxen, die nicht gehovert sind,
kleiner und durchsichtiger
*/
.box:not(:hover) {
opacity: 0.5;
scale: 0.75;
}
}
}
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 1.5rem;
box-sizing: border-box;
display: grid;
place-content: center;
min-height: 100vh;
}
Weitere Ressourcen für Animationen
https://coolcssanimation.com/ (Showcase)
https://svgartista.net/ (SVG-Animationstool)
https://3dtransforms.desandro.com/ (3D-Transformation)
https://thoughtbot.com/blog/css-animation-for-beginners (CSS-Animation für Anfänger)
https://codrops.com (Inspiration & Tutorials)
Praxis
In der heutigen Praxis zeige ich euch, wie ihr ein responsives Menü erstellen könnt. Verwendet dieses Beispiel bitte als Basis, um eure eigenen Menüs für eure Website aufzubauen und mit Transitions und Animationen zu experimentieren.
<header>
<p class="logo">Logo</p>
<nav>
<ul>
<li><a href="#">Projekte</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Kontakt</a></li>
</ul>
</nav>
<!-- Mit popovertarget wird der Button mit dem Popover verknüpft -->
<button popovertarget="mobileMenu">
<div></div>
</button>
</header>
<!-- Das popover-Attribut verwandelt ein Element in ein Popover -->
<dialog id="mobileMenu" popover>
<nav>
<ul>
<li><a href="#">Projekte</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Kontakt</a></li>
</ul>
</nav>
</dialog>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Possimus molestias, commodi dicta optio aliquam est! Tenetur, dicta sed! Minima numquam doloribus dolore debitis qui distinctio consequuntur accusamus voluptatum et reiciendis.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolores quod sint nostrum, dolorum, pariatur quae, aspernatur tenetur iste dignissimos sit voluptatem ducimus quas autem natus odio laboriosam excepturi? Molestiae, nobis!</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Ducimus corporis eaque labore optio ut ipsa at, debitis, nemo deleniti natus perferendis dignissimos ullam pariatur voluptatem earum. Magni nostrum animi totam.</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dignissimos, possimus dolore et aut quidem, mollitia minima ab unde officia harum officiis natus repellendus, atque accusantium labore. Adipisci, sed. Accusantium, magni.</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Fuga fugiat iste, sit earum rem facilis sint vero? Accusantium error repellendus, quod culpa minima aliquid tempore perspiciatis dolore, repellat tenetur quia?</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Omnis deleniti, at repudiandae voluptatum similique nihil officiis recusandae officia consectetur totam earum quaerat est laudantium incidunt, facilis sint facere necessitatibus? Dignissimos.</p>
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 4rem;
}
/* Alle Links sollen die Schriftfarbe erben */
a {
color: inherit;
}
header {
/* Am oberen Bildschirmrand fixiert */
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 1rem;
background-color: rgb(0 0 0 / 0.5);
color: white;
display: flex;
gap: 1rem;
transition: background-color 200ms ease;
/* Falls ein benachbartes <dialog>-Element angezeigt wird */
&:has(+ dialog:popover-open) {
background-color: #000;
button div {
&:before,
&:after {
/*
Wir verändern die Verzögerung,
wenn das Mobile-Menü angezeigt wird
*/
transition-delay: 0ms, 200ms;
}
/* Die horizontalen Linien werden zu einem X transformiert */
&:before {
rotate: 45deg;
translate: 0 0.1875rem;
}
&:after {
rotate: -45deg;
translate: 0 -0.1875rem;
}
}
}
.logo {
margin: 0;
margin-right: auto;
}
ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 1.5rem;
}
button {
border: none;
margin: 0;
padding: 0.5rem;
background-color: transparent;
color: inherit;
cursor: pointer;
transition: background-color 200ms ease;
/* Beim Hovern wird der Hintergrund geändert */
&:hover {
background-color: rgb(from currentColor r g b / 0.25);
}
div {
/*
Das Hamburger-Icon wird aus Pseudo-Elementen gebaut,
damit man es animieren kann
*/
&::before, &::after {
display: block;
content: '';
height: 0.125rem;
width: 1.125rem;
background-color: currentColor;
transition: translate 200ms ease 200ms, rotate 200ms ease;
}
&::before {
margin-bottom: 0.25rem;
}
}
}
}
dialog {
text-align: center;
padding-top: 1.5rem;
padding-bottom: calc(1.5rem + 3.5rem);
border: none;
background-color: black;
color: white;
margin: 0;
width: auto;
top: 3.5rem;
opacity: 0;
transition-property: overlay, opacity, display;
transition-timing-function: ease;
transition-duration: 200ms;
transition-behavior: allow-discrete;
/* Wenn das Mobile-Menü angezeigt wird, gelten diese Regeln */
&:popover-open {
opacity: 1;
/*
Wird benötigt, damit die Animation beim Erscheinen abspielt,
ansonsten weiß der Browser nicht was der Startpunkt der
Animation ist
*/
@starting-style {
opacity: 0;
}
ul li {
translate: 0;
@starting-style {
opacity: 0;
translate: 0 -1.5rem;
}
/* Mit Pseudo-Klassen wird ein Stagger-Effekt erzielt */
&:nth-child(2) {
transition-delay: 50ms;
}
&:nth-child(3) {
transition-delay: 100ms;
}
}
}
ul {
list-style: none;
margin: 0;
padding: 0;
li {
translate: 0 -1.5rem;
transition-property: translate, opacity;
transition-duration: 350ms;
transition-timing-function: ease;
a {
display: block;
padding: 0.5rem 1.5rem ;
}
}
}
}
/*
Wenn der Viewport kleiner oder gleich 640px,
dann soll das <nav>-Element nicht angezeigt werden
*/
@media screen and (width <= 40rem) {
header {
nav {
display: none;
}
}
}
/*
Wenn der Viewport größer 640px,
dann sollen der Button und der Dialog nicht angezeigt werden
*/
@media screen and (width > 40rem) {
header {
button {
display: none;
}
}
dialog {
display: none;
}
}