PFAD: ./module/css/css5/

DATEI: string(25) "00Inhaltsverzeichnis5.php"
DATEI: string(27) "35Respönsives-Webdesign.php"
DATEI: string(21) "36Flexible Größen.php"
DATEI: string(19) "38Media Queries.php"
DATEI: string(18) "39Mobile-First.php"
DATEI: string(23) "41Responsive Bilder.php"
DATEI: string(27) "42Responsive Typografie.php"
DATEI: string(25) "43Responsive Tabellen.php"
DATEI: string(23) "44Container Queries.php"
DATEI: string(21) "46Feature-Queries.php"

css mit Adi Prinz

Mobile-First-Praxisbeispiel

Das folgende Beispiel verbindet die bisherigen Grundlagen zu einem vollständigen responsiven Seitenlayout. Es enthält einen Header, eine Navigation, einen Hauptbereich mit Karten, eine Seitenleiste und einen Footer.

Die Darstellung beginnt einspaltig. Zusätzliche Spalten und die horizontale Navigation werden erst ergänzt, wenn ausreichend Platz vorhanden ist.

Ziel des Beispiels

Das Layout soll sich in drei Stufen verändern:

  • Schmale Viewports: Alle Inhalte stehen untereinander. Die Navigation wird über einen Menü-Button geöffnet.
  • Ab 48rem: Die Navigation wird horizontal dargestellt und die Karten erscheinen zweispaltig.
  • Ab 75rem: Hauptinhalt und Seitenleiste werden nebeneinander angeordnet. Die Karten erscheinen dreispaltig.
Die Breakpoints gehören nicht zu bestimmten Geräten. Sie wurden danach gewählt, wann das jeweilige Layout genügend Platz benötigt.

HTML-Grundstruktur

<header class="site-header">
  <div class="page-container header-inner">
    <a class="site-logo" href="#">EDV mit Adi Prinz</a>

    <button
      class="nav-toggle"
      type="button"
      aria-expanded="false"
      aria-controls="main-navigation"
    >
      Menü
    </button>

    <nav
      id="main-navigation"
      class="main-navigation"
      aria-label="Hauptnavigation"
    >
      <ul class="nav-list">
        <li><a href="#">Startseite</a></li>
        <li><a href="#">HTML</a></li>
        <li><a href="#">CSS</a></li>
        <li><a href="#">JavaScript</a></li>
      </ul>
    </nav>
  </div>
</header>

<main class="page-container page-layout">
  <section class="content-area">
    <h1>Responsive Webdesign</h1>

    <p class="intro">
      Lernbeispiele zu flexiblen Layouts und responsiven Medien.
    </p>

    <div class="card-grid">
      <article class="card">
        <img
          src="images/flexbox.jpg"
          alt=""
          width="800"
          height="500"
        >

        <div class="card-content">
          <h2>Flexbox</h2>
          <p>
            Eindimensionale Layouts flexibel ausrichten und verteilen.
          </p>
          <a href="#">Beispiel öffnen</a>
        </div>
      </article>

      <article class="card">
        <img
          src="images/grid.jpg"
          alt=""
          width="800"
          height="500"
        >

        <div class="card-content">
          <h2>CSS Grid</h2>
          <p>
            Zeilen und Spalten für komplexe Layouts verwenden.
          </p>
          <a href="#">Beispiel öffnen</a>
        </div>
      </article>

      <article class="card">
        <img
          src="images/media-queries.jpg"
          alt=""
          width="800"
          height="500"
        >

        <div class="card-content">
          <h2>Media Queries</h2>
          <p>
            Darstellungen an Platz und Benutzereinstellungen anpassen.
          </p>
          <a href="#">Beispiel öffnen</a>
        </div>
      </article>
    </div>
  </section>

  <aside class="sidebar">
    <h2>Weitere Themen</h2>

    <ul>
      <li><a href="#">Responsive Bilder</a></li>
      <li><a href="#">Container Queries</a></li>
      <li><a href="#">Barrierefreiheit</a></li>
    </ul>
  </aside>
</main>

<footer class="site-footer">
  <div class="page-container">
    <p>&copy; EDV mit Adi Prinz</p>
  </div>
</footer>
    

Grundlegende CSS-Regeln

Das Grundlayout gilt für alle Viewport-Breiten. Es ist bereits ohne Media Query vollständig nutzbar.

:root {
  --page-width: 75rem;
  --spacing: 1rem;
  --color-primary: #5f3dc4;
  --color-primary-dark: #4527a0;
  --color-text: #343a40;
  --color-light: #f1f3f5;
  --color-border: #ced4da;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  color: var(--color-text);
  font-family: Arial, sans-serif;
  line-height: 1.5;
  background-color: #ffffff;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

a {
  color: var(--color-primary-dark);
}

.page-container {
  width: min(100% - 2rem, var(--page-width));
  margin-inline: auto;
}
    

Die Funktion min() begrenzt den Container auf 75rem. Auf schmalen Viewports bleiben durch 100% - 2rem links und rechts jeweils ungefähr 1rem Abstand.

Header und Navigation für schmale Viewports

.site-header {
  color: #ffffff;
  background-color: var(--color-primary);
}

.header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1rem;
}

.site-logo {
  color: inherit;
  font-size: 1.25rem;
  font-weight: 700;
  text-decoration: none;
}

.nav-toggle {
  padding: 0.6rem 1rem;
  color: #ffffff;
  font: inherit;
  background-color: transparent;
  border: 1px solid currentColor;
  border-radius: 0.25rem;
  cursor: pointer;
}

.main-navigation {
  display: none;
  flex-basis: 100%;
}

.main-navigation.is-open {
  display: block;
}

.nav-list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-list a {
  display: block;
  padding: 0.75rem;
  color: #ffffff;
  text-decoration: none;
  border-radius: 0.25rem;
}

.nav-list a:hover,
.nav-list a:focus-visible {
  background-color: var(--color-primary-dark);
}
    

Die Navigation ist zunächst ausgeblendet. Durch die Klasse is-open wird sie unterhalb des Logos und des Menü-Buttons eingeblendet.

Inhaltsbereich und Karten

.page-layout {
  display: grid;
  gap: 2rem;
  padding-block: 2rem;
}

.intro {
  max-width: 65ch;
}

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}

.card {
  overflow: hidden;
  background-color: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  box-shadow: 0 0.25rem 0.75rem rgb(0 0 0 / 10%);
}

.card img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
}

.card-content {
  padding: 1rem;
}

.card-content h2 {
  margin-top: 0;
}

.card-content a {
  font-weight: 700;
}

.sidebar {
  padding: 1.25rem;
  background-color: var(--color-light);
  border-radius: 0.5rem;
}

.sidebar h2 {
  margin-top: 0;
}

.sidebar ul {
  margin-bottom: 0;
  padding-left: 1.25rem;
}
    

Sowohl das äußere Seitenlayout als auch das Kartenlayout besitzen zunächst nur eine Spalte. Dadurch bleiben alle Inhalte auf schmalen Viewports gut lesbar.

Erster Breakpoint ab 48rem

@media (min-width: 48rem) {
  .nav-toggle {
    display: none;
  }

  .main-navigation,
  .main-navigation.is-open {
    display: block;
    flex-basis: auto;
  }

  .nav-list {
    flex-direction: row;
    align-items: center;
  }

  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
    

Ab 48rem ändern sich zwei Bereiche:

  • Der Menü-Button wird ausgeblendet und die Navigation erscheint horizontal.
  • Die Karten werden in zwei gleich breite Spalten aufgeteilt.
Eigenschaften wie Farben, Abstände und Rahmen werden nicht wiederholt, weil sie sich am Breakpoint nicht ändern.

Zweiter Breakpoint ab 75rem

@media (min-width: 75rem) {
  .page-layout {
    grid-template-columns: minmax(0, 1fr) 18rem;
    align-items: start;
  }

  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .sidebar {
    position: sticky;
    top: 1rem;
  }
}
    

Auf breiten Viewports erhält die Seitenleiste eine feste Spaltenbreite von 18rem. Der Hauptinhalt verwendet den verbleibenden Platz.

minmax(0, 1fr) erlaubt der Hauptspalte, tatsächlich schmaler zu werden, wenn ein Inhalt andernfalls über die verfügbare Breite hinausragen würde.

Die Seitenleiste bleibt mit position: sticky beim Scrollen sichtbar, solange innerhalb ihres übergeordneten Bereichs genügend Platz vorhanden ist.

JavaScript für den Menü-Button

const navToggle = document.querySelector(".nav-toggle");
const navigation = document.querySelector(".main-navigation");

if (navToggle && navigation) {
  navToggle.addEventListener("click", () => {
    const isOpen = navigation.classList.toggle("is-open");

    navToggle.setAttribute("aria-expanded", String(isOpen));
  });
}
    

Beim Anklicken wird die Klasse is-open umgeschaltet. Gleichzeitig informiert aria-expanded assistive Technologien darüber, ob die Navigation geöffnet oder geschlossen ist.

Ohne JavaScript bleibt die mobile Navigation in diesem Beispiel geschlossen. Für produktive Webseiten kann eine zusätzliche Fallback-Lösung sinnvoll sein.

Navigation beim Wechsel der Viewport-Breite zurücksetzen

Wird die Navigation auf einem schmalen Viewport geöffnet und anschließend das Browserfenster vergrößert, bleibt die Klasse is-open bestehen. Die Darstellung funktioniert zwar weiterhin, der Zustand kann jedoch bereinigt werden.

const desktopNavigation = window.matchMedia("(min-width: 48rem)");

function resetNavigation(event) {
  if (event.matches) {
    navigation.classList.remove("is-open");
    navToggle.setAttribute("aria-expanded", "false");
  }
}

desktopNavigation.addEventListener("change", resetNavigation);
    

Die Funktion wird ausgeführt, wenn die Media Query ihren Zustand ändert. Beim Wechsel zur breiten Navigation wird der mobile Öffnungszustand zurückgesetzt.

Fokusdarstellung ergänzen

a:focus-visible,
button:focus-visible {
  outline: 3px solid #ffca2c;
  outline-offset: 3px;
}
    

Die Fokusmarkierung zeigt bei der Bedienung mit der Tastatur, welches Element gerade aktiv ist. Sie sollte sich deutlich vom Hintergrund unterscheiden.

Eine vorhandene Fokusmarkierung sollte niemals ersatzlos mit outline: none entfernt werden.

Bewegungen auf geeignete Geräte beschränken

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .card {
    transition:
      transform 200ms ease,
      box-shadow 200ms ease;
  }

  .card:hover {
    transform: translateY(-0.25rem);
    box-shadow: 0 0.5rem 1.25rem rgb(0 0 0 / 18%);
  }
}
    

Der Hover-Effekt wird nur verwendet, wenn das primäre Eingabegerät sinnvoll hovern kann und keine reduzierte Bewegung gewünscht wurde.

Footer

.site-footer {
  padding-block: 1.5rem;
  color: #ffffff;
  background-color: #343a40;
}

.site-footer p {
  margin: 0;
}
    

Reihenfolge der CSS-Regeln

Das Stylesheet kann in folgender Reihenfolge aufgebaut werden:

  1. Custom Properties und allgemeine Grundeinstellungen
  2. Container und gemeinsame Hilfsklassen
  3. Header und Navigation
  4. Hauptbereich, Karten und Seitenleiste
  5. Footer
  6. Fokus- und Interaktionszustände
  7. Media Queries von klein nach groß
  8. zusätzliche Abfragen für Hover und reduzierte Bewegung
Eine feste Gliederung erleichtert das spätere Auffinden von Regeln und macht Überschreibungen an Breakpoints besser nachvollziehbar.

Das Layout testen

Beim Testen sollten nicht nur die drei festgelegten Stufen geprüft werden:

  • Viewport langsam verbreitern und verkleinern.
  • Navigation mit Maus, Tastatur und nach Möglichkeit Touch bedienen.
  • Texte im Browser deutlich vergrößern.
  • lange Überschriften und Navigationseinträge verwenden.
  • fehlende oder langsam ladende Bilder berücksichtigen.
  • Hochformat und Querformat prüfen.
  • Druckvorschau kontrollieren.
  • reduzierte Bewegung und dunkles Farbschema simulieren.

Häufige Fehler

  • Das schmale Grundlayout ist ohne Media Query nicht vollständig nutzbar.
  • Der Menü-Button wird auf großen Viewports nicht ausgeblendet.
  • Die Navigation wird zwar optisch geöffnet, aber aria-expanded nicht aktualisiert.
  • Karten erhalten feste Breiten und ragen auf schmalen Viewports aus dem Container heraus.
  • Hauptinhalt und Seitenleiste werden zu früh nebeneinander dargestellt.
  • Unveränderte Eigenschaften werden in jeder Media Query wiederholt.
  • Hover-Effekte werden auch auf Geräten ohne sinnvolle Hover-Funktion vorausgesetzt.
  • Das Layout wird nur an den exakten Breakpoint-Werten getestet.

Zusammenfassung

  • Das Grundlayout ist einspaltig und ohne Media Query vollständig verwendbar.
  • Ab 48rem werden die Navigation horizontal und die Karten zweispaltig dargestellt.
  • Ab 75rem werden Hauptinhalt und Seitenleiste nebeneinander sowie die Karten dreispaltig dargestellt.
  • Jede Media Query enthält nur die Regeln, die sich am jeweiligen Breakpoint tatsächlich ändern.
  • Flexbox übernimmt die Navigation, während Grid das Seiten- und Kartenlayout steuert.
  • JavaScript schaltet die mobile Navigation und aktualisiert gleichzeitig den zugänglichen Zustand.
  • Fokusdarstellungen, Hover-Fähigkeit und reduzierte Bewegung werden berücksichtigt.
Ein gutes Mobile-First-Layout beginnt mit einer einfachen, vollständigen Grunddarstellung und ergänzt komplexere Anordnungen erst dann, wenn der Inhalt ausreichend Platz besitzt.