CSS-Mathefunktionen round(), mod() und rem() in der Praxis nutzen
AI generated
{ }
@
CSS · Mathefunktionen · Layout
round(), mod() und rem()
Native CSS-Mathematik für Rastergrößen und zyklische Werte

Mit round(), mod() und rem() rechnet der Browser direkt in der Stylesheet-Deklaration, ohne dass ein Skript vorher Werte ausrechnen und als Custom Property injizieren muss. Wer Rastergrößen sauber runden oder Werte zyklisch wiederholen lassen will, etwa Karussell-Indizes oder Streifenmuster, findet in diesen drei Funktionen ein Werkzeug, das direkt in calc() eingebettet werden kann.

12 Min. Lesezeit round() · mod() · rem() CSS Values and Units Level 4

1. Warum CSS jetzt eigene Rundungs- und Modulo-Funktionen braucht

Bis vor kurzem gab es in CSS keine Möglichkeit, einen berechneten Wert auf eine bestimmte Schrittweite zu runden oder eine zyklische Wiederholung abzubilden, ohne auf JavaScript oder eine feste Anzahl handgeschriebener Selektoren zurückzugreifen. Ein Layout, das Elemente auf ein 8-Pixel-Raster zwingen sollte, musste entweder jeden Wert von Hand passend wählen oder ein Skript beim Laden der Seite Werte berechnen und als style-Attribut injizieren lassen. Das funktioniert, bricht aber jedes Mal, wenn sich die Bildschirmbreite oder eine andere Eingabegröße ändert, auf die CSS selbst reagieren könnte.

Mit round(), mod() und rem() aus den CSS Values and Units Level 4 bekommt die Sprache genau diese Fähigkeit direkt eingebaut. Alle drei Funktionen sind reine Mathefunktionen, die wie calc() überall dort stehen können, wo ein numerischer Wert erwartet wird, und die sich beliebig mit calc(), Custom Properties und sogar Container-Query-Einheiten kombinieren lassen. Das verschiebt Logik, die früher zwingend im JavaScript lag, zurück in die Deklaration, wo sie leichter zu warten und schneller im Rendering ist.

2. round(): Werte auf eine Schrittweite runden

Die Funktion round(<strategy>, <value>, <interval>) rundet einen Wert auf ein Vielfaches des angegebenen Intervalls. Die Strategie bestimmt die Rundungsrichtung: nearest rundet zum nächsten Vielfachen, up immer aufwärts, down immer abwärts, und to-zero rundet in Richtung Null. Wird die Strategie weggelassen, verhält sich round() wie round(nearest, ...).

Praktisch wird das, sobald ein Layout auf ein festes Grundraster ausgerichtet werden soll, etwa ein 8-Pixel-Spacing-System. Statt jede Breite einzeln von Hand auf ein Vielfaches von 8 zu bringen, übernimmt round() das für berechnete, dynamische Werte, zum Beispiel wenn die Breite eines Elements von der Viewport-Breite abhängt und trotzdem stets auf dem Raster landen soll.


.grid-item {
  /* Element-Breite haengt vom Viewport ab, landet aber immer
     auf einem Vielfachen von 8px */
  width: round(nearest, 100vw / 3, 8px);
}

.card {
  /* Padding wird nie kleiner als 8px, aber immer auf 8px-Schritte
     nach oben gerundet -- verhindert krumme Zwischenwerte */
  padding: round(up, 1.2rem, 8px);
}

3. mod(): zyklische Werte für Muster und Indizes

mod(A, B) berechnet den Rest einer Division von A durch B und übernimmt dabei das Vorzeichen von B. Das Ergebnis liegt also immer im gleichen Vorzeichenbereich wie der Divisor, unabhängig davon, ob A positiv oder negativ ist. Das entspricht der klassischen mathematischen Modulo-Operation, wie man sie aus Python kennt, und eignet sich damit perfekt für zyklische Muster, bei denen negative Zwischenwerte trotzdem sinnvoll umlaufen sollen.

Ein typischer Anwendungsfall ist ein Karussell oder eine Galerie, bei der ein aktiver Index über eine Custom Property gesetzt wird und CSS daraus einen zyklischen Zustand ableiten soll, etwa um jede dritte Kachel farblich hervorzuheben, egal wie hoch der Index insgesamt zählt. Auch bei Streifenmustern mit nth-child-ähnlicher Logik über berechnete Werte hilft mod(), den Umlauf ohne zusätzliche Klassen abzubilden.


.carousel {
  --active-index: 7;
  /* Reduziert einen beliebig hohen Index auf den Bereich 0..2,
     um z.B. immer eines von drei Zustandsfarben zu waehlen */
  --cycle: mod(var(--active-index), 3);
}

.stripe {
  /* Farbverlauf-Offset laeuft zyklisch, negative Werte
     bleiben trotzdem im 0..359-Bereich */
  --hue-offset: mod(var(--index) * 47, 360deg);
  background: hsl(var(--hue-offset) 70% 50%);
}

4. rem() vs. mod(): der Unterschied bei negativen Zahlen

Auf den ersten Blick sehen mod() und rem(A, B) identisch aus, denn beide berechnen einen Divisionsrest. Der entscheidende Unterschied zeigt sich erst bei negativen Eingabewerten: rem() übernimmt das Vorzeichen von A, dem Dividend, während mod() das Vorzeichen von B, dem Divisor, übernimmt. Bei ausschließlich positiven Werten liefern beide Funktionen exakt dasselbe Ergebnis, weshalb der Unterschied in vielen Tutorials unter den Tisch fällt.

Für ein Layout, das mit negativen Verschiebungen arbeitet, zum Beispiel eine Position, die relativ zu einem Ankerpunkt nach links oder rechts wandern kann, liefert rem() häufig das intuitivere Ergebnis, weil ein negativer Eingabewert auch ein negatives Ergebnis erzeugt, statt in den positiven Bereich zu springen. Für zyklische Zustände wie Karussell-Indizes ist dagegen fast immer mod() die richtige Wahl, weil ein sauberer, stets positiver Umlauf gewünscht ist.

5. Kombination mit calc(): verschachtelte Ausdrücke

Alle drei Funktionen sind vollwertige mathematische CSS-Ausdrücke und lassen sich beliebig in calc() verschachteln oder selbst wieder als Argument einer anderen Mathefunktion verwenden. Das erlaubt komplexe Layout-Regeln in einer einzigen Deklaration, etwa eine Breite, die zuerst über mod() einen zyklischen Offset erzeugt und diesen dann per calc() mit einer festen Basisgröße verrechnet.

Wichtig ist, dass die Argumente typkompatibel bleiben: Wer Pixel mit Grad mischt, ohne eine gemeinsame Einheit oder eine bewusste Umrechnung vorzusehen, bekommt einen ungültigen Wert, den der Browser stillschweigend ignoriert. Deshalb lohnt es sich, Custom Properties für Zwischenwerte konsequent mit expliziter Einheit zu deklarieren, statt unitless Zahlen quer durch verschachtelte calc()-Ausdrücke zu reichen.


.pattern-tile {
  --i: 5;
  /* mod() erzeugt einen zyklischen Offset 0..2, calc() addiert
     darauf eine feste Basisbreite -- alles in einer Deklaration */
  width: calc(120px + mod(var(--i), 3) * 20px);
}

.snapped {
  /* verschachtelt: erst runden, dann das Ergebnis in calc()
     weiterverwenden */
  margin-top: calc(round(nearest, var(--raw-offset), 4px) + 2px);
}

6. Browser-Unterstützung und Feature-Detection

round(), mod() und rem() sind seit 2023/2024 in aktuellen Versionen von Chrome, Edge, Firefox und Safari implementiert und gelten inzwischen als produktionsreif für Projekte, die keine sehr alten Browser-Versionen mehr unterstützen müssen. Für Projekte mit strengeren Support-Anforderungen lohnt sich vor dem Einsatz ein Blick auf die aktuellen Zahlen, da sich die Untergrenze der unterstützten Versionen je nach Browser unterscheidet.

Eine zuverlässige Absicherung bietet @supports: Innerhalb einer @supports (width: round(nearest, 1px, 1px))-Regel lässt sich prüfen, ob der Browser die Funktion versteht, und ein Fallback-Wert außerhalb der Regel greift automatisch für ältere Browser. So bleibt das Layout auch ohne native Rundung funktional, nur eben ohne die exakte Rasterausrichtung.


.grid-item {
  /* Fallback fuer Browser ohne round()-Support */
  width: 33.33vw;
}

@supports (width: round(nearest, 1px, 1px)) {
  .grid-item {
    width: round(nearest, 100vw / 3, 8px);
  }
}

7. Praxisbeispiel: ein CSS-Grid mit garantiert ganzzahligen Spurbreiten

Ein häufiges Problem bei responsiven Grids sind Sub-Pixel-Breiten, die zu leicht sichtbaren Verschiebungen zwischen Grid-Spuren führen können, besonders bei Bildergalerien mit sichtbaren Rahmen. Mit round() lässt sich jede Spurbreite explizit auf eine ganze Pixelzahl zwingen, statt sich auf die Sub-Pixel-Rundung des Browsers zu verlassen, die je nach Rendering-Engine leicht unterschiedlich ausfallen kann.

In der Praxis kombiniert man das oft mit clamp(), um zusätzlich eine Mindest- und Maximalbreite festzulegen, während round() für die Rasterausrichtung innerhalb dieser Grenzen sorgt. Das Ergebnis ist ein Grid, das auf jedem Viewport sauber aussieht, ohne dass JavaScript beim Resize-Event nachrechnen muss.

8. Praxisbeispiel: Karussell-Zustände mit mod() ohne JavaScript-Zähler

Ein Bildkarussell mit mehreren sichtbaren Kacheln muss oft wissen, welche Position eine Kachel relativ zur aktuell aktiven Kachel einnimmt, um sie entsprechend zu skalieren oder abzudunkeln. Mit mod() lässt sich dieser relative Zustand direkt aus einer laufenden Nummer und der aktuellen Indexposition berechnen, ohne dass JavaScript jede Kachel einzeln mit einer Klasse markieren muss.

Das reduziert die Menge an DOM-Manipulation drastisch: JavaScript setzt nur noch eine einzige Custom Property für den aktiven Index, und CSS berechnet über mod() für jede Kachel selbstständig, ob sie aktiv, benachbart oder weit entfernt ist. Das hält die Interaktionslogik schlank und die Style-Berechnung dort, wo sie am schnellsten ausgeführt wird.


.carousel-item {
  --distance: mod(var(--item-index) - var(--active-index) + var(--total), var(--total));
}

.carousel-item[style*="--distance: 0"] {
  transform: scale(1.1);
  opacity: 1;
}

9. Grenzen und typische Stolperfallen

Eine häufige Falle ist die Annahme, round(), mod() und rem() könnten wie JavaScript-Funktionen zur Laufzeit beliebig komplexe Logik abbilden. Tatsächlich sind es reine, deklarative Mathefunktionen ohne Bedingungen, Schleifen oder Zustandsverwaltung, weshalb komplexere Zustandsmaschinen weiterhin auf Custom Properties und gezielte Selektoren angewiesen bleiben. Für einfache Rundungs- und Zyklus-Logik reichen sie aber vollständig aus und ersetzen genau den Teil, für den früher JavaScript nötig war.

Eine zweite Stolperfalle ist die Division durch Null: mod(A, 0) und rem(A, 0) ergeben einen ungültigen Wert, den der Browser wie eine fehlerhafte Deklaration behandelt und ignoriert. Wer den Divisor aus einer Custom Property bezieht, die theoretisch auf Null gesetzt werden könnte, sollte einen Fallback über var(--divisor, 1) vorsehen, um dieses Szenario abzufangen.

Funktion Berechnung Vorzeichen des Ergebnisses Typischer Einsatz
round(nearest, A, B) Rundet A auf ein Vielfaches von B wie A Rastergrößen, Spacing-Systeme
round(up, A, B) Rundet A immer aufwärts auf B wie A Mindestabstände sicherstellen
mod(A, B) Divisionsrest von A / B wie B, der Divisor Zyklische Muster, Indizes
rem(A, B) Divisionsrest von A / B wie A, der Dividend Relative Verschiebungen mit negativen Werten

Mironsoft

Modernes CSS, Layout-Architektur und Rendering-Performance

CSS, das wartbar bleibt statt mit jeder Änderung zu brechen?

Wir prüfen bestehende Stylesheets auf Spezifitäts-Chaos und Layout-Thrashing und bauen daraus eine CSS-Architektur mit Cascade Layers, Custom Properties und modernen Layout-Primitiven, die auch nach dem zehnten Feature noch verständlich ist.

CSS-Audit

Spezifität, Cascade-Konflikte und ungenutzte Selektoren systematisch aufdecken.

Architektur-Refactoring

Cascade Layers, Custom Properties und Design Tokens sauber einführen.

Performance-Tuning

Layout-Thrashing, teure Selektoren und Rendering-Engpässe gezielt beheben.

10. Zusammenfassung

round(), mod() und rem() in CSS: Das Wichtigste auf einen Blick

round()

Rundet einen Wert auf ein Vielfaches eines Intervalls, mit den Strategien nearest, up, down und to-zero.

mod()

Divisionsrest, der das Vorzeichen des Divisors übernimmt, ideal für zyklische Zustände wie Karussell-Indizes.

rem()

Divisionsrest, der das Vorzeichen des Dividend übernimmt, passender bei negativen relativen Verschiebungen.

Kombination

Alle drei Funktionen lassen sich beliebig in calc() verschachteln, solange die Einheiten der Argumente kompatibel bleiben.

11. FAQ: round(), mod() und rem() in CSS: Das Wichtigste auf einen Blick

1Was macht round() in CSS?
round() rundet einen berechneten Wert auf ein Vielfaches eines angegebenen Intervalls, mit wählbarer Strategie: nearest, up, down oder to-zero.
2Wie unterscheiden sich mod() und rem()?
Beide berechnen einen Divisionsrest, aber mod() übernimmt das Vorzeichen des Divisors, während rem() das Vorzeichen des Dividend übernimmt. Bei positiven Zahlen sind die Ergebnisse identisch.
3Wann sollte ich mod() statt rem() verwenden?
Für zyklische Zustände wie Karussell-Indizes oder Farbverlauf-Offsets, bei denen das Ergebnis immer im positiven Bereich bleiben soll, ist mod() die richtige Wahl.
4Kann ich round(), mod() und rem() in calc() verschachteln?
Ja, alle drei sind vollwertige mathematische Ausdrücke und lassen sich beliebig in calc() oder ineinander verschachteln, solange die Einheiten der Argumente zueinander passen.
5Was passiert bei Division durch Null?
mod(A, 0) und rem(A, 0) ergeben einen ungültigen Wert, den der Browser wie eine fehlerhafte Deklaration ignoriert. Ein Fallback über var(--divisor, 1) beugt dem vor.
6Unterstützen alle modernen Browser diese Funktionen?
Chrome, Edge, Firefox und Safari unterstützen round(), mod() und rem() seit 2023/2024 in aktuellen Versionen. Für ältere Browser empfiehlt sich eine Absicherung über @supports.
7Wie prüfe ich Browser-Support ohne JavaScript?
Mit einer @supports-Regel, die die Funktion selbst als Testbedingung nutzt, etwa @supports (width: round(nearest, 1px, 1px)). Außerhalb der Regel steht ein einfacher Fallback-Wert.
8Ersetzen diese Funktionen JavaScript vollständig?
Nein, nur den Teil, der reine Rundungs- oder Modulo-Berechnung ist. Komplexere Zustandslogik mit Bedingungen oder Schleifen bleibt weiterhin Aufgabe von JavaScript.
9Kann ich negative Werte in round() verwenden?
Ja, round() funktioniert auch mit negativen Eingabewerten und rundet entsprechend der gewählten Strategie in die passende Richtung.
10Eignet sich round() für Rastergrößen in Design-Systemen?
Ja, das ist einer der Hauptanwendungsfälle: dynamische, berechnete Breiten oder Abstände lassen sich damit zuverlässig auf ein festes Spacing-Raster zwingen.