Verwendung von CSS typisierter Arithmetik
CSS typisierte Arithmetik bezieht sich auf zulässige Berechnungen, die mit typisierten CSS-Werten über Funktionen wie calc() durchgeführt werden, und insbesondere auf das Verhalten, das im Modul CSS Werte und Einheiten definiert ist. CSS typisierte Arithmetik ermöglicht es, einen Wert mit einer Einheit durch einen Wert mit einer anderen Einheit desselben Datentyps zu dividieren, was zu einheitenlosen Quotienten führt.
Diese Quotienten können dann als <number> in den Werten einheitenloser Eigenschaften verwendet oder durch Multiplizieren mit einem numerischen typisierten Wert in jeden numerischen Datentyp (wie ein <length>, <percentage> oder <angle>) umgewandelt werden.
Dieses Verhalten der typisierten Arithmetik ermöglicht es, Beziehungen zwischen verschiedenen Werten auf einer Seite zu erstellen. Dieser Artikel untersucht die typisierte Arithmetik und präsentiert mehrere Beispiele, die sie nutzen.
Hinweis: Ursprünglich beschränkte das Modul CSS Werte und Einheiten die Argumente für Multiplikations- und Divisionoperationen, um die Erkennung von Divisionen durch Null zur Parserzeit zu ermöglichen und Probleme wie quadratische Einheiten zu vermeiden. Die typisierte Arithmetik lockert einige dieser Einschränkungen in unterstützten Browsern.
Regeln für CSS typisierte Arithmetik
Beim Ausführen von Berechnungen auf Werten in CSS gibt es einige Regeln bezüglich der Kompatibilität von Werten mit unterschiedlichen Datentypen.
Addition und Subtraktion
Beim Addieren oder Subtrahieren von Werten müssen alle Werte innerhalb desselben übergeordneten Datentyps liegen. Die folgenden Beispiele produzieren alle gültige Ergebnisse:
/* <length-percentage> units */
calc(250px - 150px)
calc(100% - 50px)
calc(50vw + 2rem)
calc(25cqw + 3in)
/* <angle> units */
calc(40deg + 2rad)
calc(420deg - 1turn)
Die folgenden sind jedoch ungültig. Die Ergebnisse solcher gemischter Typberechnungen wären bedeutungslos:
calc(200px + 100ms)
calc(50% + 90deg)
Multiplikation
Beim Multiplizieren von Werten in CSS darf nur einer der Werte eine Einheit haben. Alle anderen Werte müssen einheitenlose <number>-Werte sein. Dies liegt daran, dass Sie ein Produkt haben möchten, das eine gröÃere oder kleinere Menge derselben Einheit ist, und keine quadratischen Einheiten erstellen möchten, die von keinen CSS-Eigenschaften akzeptiert werden.
calc(200px * 4) /* 800px */
calc(60deg * 3) /* 180deg */
Wenn Sie versuchen, zwei Werte mit Einheiten zu multiplizieren - selbst mit denselben Einheiten - gelten die normalen mathematischen Regeln, dass die Einheiten ebenfalls multipliziert werden sollten. Zum Beispiel:
calc(200px * 4px)
In diesem Fall ist die Funktion ungültig, da 800px2 in CSS bedeutungslos ist.
Division
Beim Dividieren von Werten in CSS können Sie einen Wert mit einer Einheit durch einen einheitenlosen Wert teilen:
calc(1000px / 2) /* 500px */
calc(360deg / 4) /* 90deg */
Sie können jedoch keinen einheitenlosen Wert durch einen Wert mit einer Einheit teilen, da das keinen Sinn ergibt:
calc(1000 / 2px) /* ?!? */
Wenn ein Wert eines beliebigen numerischen Datentyps durch einen Wert desselben Typs geteilt wird, heben sich die Einheiten gegenseitig auf, und man erhält einen einheitenlosen Wert. Im Hintergrund werden die beiden Werte berechnet, bevor sie durch einander geteilt werden.
Infolgedessen kann dieselbe Berechnung sehr unterschiedliche Ergebnisse haben, abhängig davon, in welchem Kontext sie verwendet wird und welchen Einheiten der Divisor hat.
Betrachten Sie das folgende Beispiel:
calc(100vw / 1px)
Die 100vw entsprechen 100% der Breite des Ansichtsfensters. Wenn das Ansichtsfenster derzeit 1000px breit ist, ergibt die Berechnung den einheitenlosen Wert 1000. Ist das Ansichtsfenster 500px breit, ergibt die Berechnung den einheitenlosen Wert 500.
Wenn wir den 1px-Divisor durch 1em ersetzen, erhalten wir sehr unterschiedliche Ergebnisse:
calc(100vw / 1em)
Wenn das Ansichtsfenster 1000px breit ist und 1em zum Zeitpunkt der Berechnung dem Browser-Standard von 16px entspricht, ergibt die vorherige Berechnung 1000px / 16px = 62.5.
Früher war es so, dass man einen typisierten Wert nicht durch einen anderen teilen konnte, selbst wenn die beiden Werte Einheiten desselben Typs hatten. Die Spezifikation wurde jedoch aktualisiert, um dies zu erlauben; prüfen Sie die Browser-Kompatibilität.
Warum ist das Verhaltensupdate nützlich?
Die Möglichkeit, einen typisierten Wert durch einen anderen desselben Typs zu teilen, mag auf den ersten Blick nicht sehr bedeutsam erscheinen, aber sie ermöglicht nützliche Assoziationen zwischen verschiedenen Werten, die zur Erstellung von responsiven UI-Funktionen verwendet werden können.
Der Schlüssel zu all dem ist, Werte in einer einheitenlosen Form über solche Divisionen darstellen zu können:
--viewport-width-in-pixels: calc(100vw / 1px);
Das Ergebnis ist ein <number>, das die Ansichtsfensterbreite in Pixeln als einheitenlosen Wert darstellt. Dieses kann überall verwendet werden, wo eine Zahl gültig ist, einschlieÃlich anderer calc()-Funktionen. Sie können andere Eigenschaftswerte dynamisch basierend auf diesem Wert variieren, unabhängig davon, welche Einheiten sie haben.
Zum Beispiel kann der einheitenlose Wert sofort auf opacity übertragen werden:
opacity: calc(var(--viewport-width-in-pixels) / 1000 - 0.5);
Sie können ihn mit einem Wert wie 1deg multiplizieren, um einen <angle>-Wert zu erstellen:
rotate: calc(var(--viewport-width-in-pixels) * 1deg);
Sie können ihn mit einem Wert wie 1rem multiplizieren, um einen <length>-Wert zu erstellen:
font-size: calc(var(--viewport-width-in-pixels) * 1rem / 200);
Lassen Sie uns einige Beispiele durchgehen, um zu zeigen, wie nützlich CSS typisierte Arithmetik sein kann.
Einfaches Beispiel: Responsive Hintergrund-Transparenz
Das Beispiel Responsive Hintergrund-Transparenz (Quellcode ansehen) zeigt, wie die Transparenz eines Hintergrundbildes variiert werden kann, wenn sich die Breite des Ansichtsfensters ändert. Wenn das Hintergrundbild beim Verkleinern des Bildschirms verblasst, wird die Wahrscheinlichkeit verringert, dass das Bild die Lesbarkeit des Textinhalts beeinträchtigt, wenn sie anfangen, sich zu überschneiden.
HTML
Das HTML enthält einige grundlegende Textinhalte, die in ein <div>-Element eingebettet sind.
<div class="wrapper">
<h1>Prose of the century</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci,
pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at
ultricies tellus laoreet sit amet.
</p>
</div>
CSS
Wir beginnen mit der Definition einer CSS-Benutzereigenschaft namens --width-percentage auf dem :root-Element, das das Ergebnis der Berechnung 100vw / 2000px enthält. Dieser Wert stellt die Ansichtsfensterbreite dar als Prozentsatz von 2000px, den wir später als Alpha-Kanalwert verwenden werden. Wenn das Ansichtsfenster 2000px breit ist, ergibt die Berechnung 1, was 100% Alpha entspricht. Alles, was weniger als 2000px Ansichtsfensterbreite ist, führt zu einem kleineren Wert.
:root {
--width-percentage: calc((100vw / 2000px));
}
Hinweis:
Jeder Alpha-Wert gröÃer als 1 wird als 1 behandelt, daher müssen wir den Maximalwert nicht begrenzen.
Wir setzen dann eine feste width und etwas padding auf das umschlieÃende <div> und zentrieren es horizontal mit margin.
.wrapper {
width: 480px;
padding: 20px;
margin: 0 auto;
}
SchlieÃlich setzen wir mehrere Hintergründe auf das <body>-Element. Wir gehen sie rückwärts durch:
- Der letzte und damit unterste
background-Wert ist ein sich nicht wiederholendes Hintergrundbild, das sich in der Nähe der oberen rechten Ecke befindet. - Der oberste Hintergrund ist ein weiÃer, halbtransparenter Ãberlagerungseffekt, der mit der
linear-gradient()-Funktion erstellt wurde, bei der beide Farbverläufe auf die gleiche Farbe gesetzt sind. Der Alpha-Kanalwert der Farbe wird als1minus der--width-percentage-Benutzereigenschaft berechnet, die wir zuvor festgelegt haben: Wenn die Ansichtsfensterbreite schmaler wird, wird--width-percentagegeringer, wodurch der weiÃe Gradient undurchsichtiger wird und die Transparenz des Hintergrundbildes reduziert.
body {
background:
linear-gradient(
rgb(255 255 255 / calc(1 - var(--width-percentage))),
rgb(255 255 255 / calc(1 - var(--width-percentage)))
),
url("https://mdn.github.io/shared-assets/images/examples/colorful-heart.png")
no-repeat top 50px right 50px;
}
Ergebnis
Um das Ergebnis zu sehen, sehen Sie sich unser Live-Beispiel für die Responsive Hintergrund-Transparenz an (Quellcode ansehen). Versuchen Sie, die Ansichtsfensterbreite zu ändern, um zu sehen, wie sich die Hintergrund-Transparenz ändert.
Variierende Werte unterschiedlicher Typen basierend auf einem einzelnen Wert
Das Beispiel Variationen unterschiedlicher Typen (Quellcode ansehen) funktioniert sehr ähnlich wie das vorhergehende Beispiel. In diesem Fall zeigen wir jedoch die Verwendung eines einzelnen Variablenwertes zur Definition mehrerer anderer Werte unterschiedlicher Datentypen.
Insbesondere setzen wir die Hintergrundfarbe und die SchriftgröÃe eines Absatzes basierend auf der Ansichtsfensterbreite.
HTML
Das Markup für dieses Beispiel enthält einen Absatz mit Text:
<p>Hello there!</p>
CSS
Wir beginnen unser CSS ähnlich wie beim vorherigen Demo, indem wir einen einheitenlosen Wert basierend auf der Ansichtsfensterbreite erstellen. Dieses Mal berechnen wir ihn, indem wir 100vw durch 1px dividieren und die resultierende Zahl in einer Benutzereigenschaft namens --viewport-in-pixels speichern. Der Wert stellt die aktuelle Ansichtsfensterbreite in Pixeln dar, ohne jegliche Einheiten.
:root {
--viewport-in-pixels: calc(100vw / 1px);
}
Nun zur Gestaltung des Absatzes selbst. Wir geben ihm zunächst einige grundlegende Stile (einen border und ein text-align von center), dann setzen wir zwei Werte basierend auf der zuvor erstellten --viewport-in-pixels-Eigenschaft:
- Wir setzen die
font-sizedes Absatzes auf einen Wert, der gleich--viewport-in-pixelsgeteilt durch200, multipliziert mit1em, ist, um den numerischen Quotienten inems umzuwandeln. - Wir setzen die
background-colordes Absatzes auf einenlch()Farbwert. Die Leuchtkraft- und Chroma-Komponenten sind konstante Werte (75%und50%), während die Farbton-Komponente auf--viewport-in-pixelsgeteilt durch10, plus100gesetzt ist. Wir multiplizieren das Ergebnis mit1deg, um sicherzustellen, dass der Wert ein<angle>ist.Hinweis: Dieser letzte Schritt ist nicht strikt notwendig, da
lch()auch einheitenlose Farbtonwerte akzeptiert. Ein Gradwert kann jedoch intuitiver sein, und wir wollten ein weiteres Beispiel zeigen, wie der einheitenlose Wert in einen anderen Datentyp umgewandelt werden kann.
p {
border: 5px solid black;
text-align: center;
font-size: calc(1em * (var(--viewport-in-pixels) / 200));
background-color: lch(
75% 50% calc((100 + (var(--viewport-in-pixels) / 10)) * 1deg)
);
}
Ergebnis
Um das Ergebnis zu sehen, sehen Sie sich unser Live-Beispiel für Variationen unterschiedlicher Typen an (Quellcode ansehen). Versuchen Sie, die Ansichtsfensterbreite zu ändern, um zu sehen, wie sich die SchriftgröÃe und Hintergrundfarbe des Absatzes ändern.
Ein animierter Geschichtenkreis
Das Beispiel animierter Geschichtenkreis (Quellcode ansehen) zeigt einen komplexeren Effekt, der durch die Nutzung der CSS-Regeln der typisierten Arithmetik erstellt wurde. In diesem Fall sind die verschiedenen Absätze eines Textkörpers in einem Kreis angeordnet, der sich von einem Mittelpunkt aus ausbreitet. Darüber hinaus schlieÃt sich der Kreis der Absätze zu einer Fächerform, wenn das Ansichtsfenster schmaler wird, und öffnet sich wieder, wenn es breiter wird.
HTML
Das HTML ist ziemlich einfach â ein <div>, der mehrere <p>-Elemente enthält. Der Text des ersten Absatzes ist in ein <strong>-Element eingeschlossen, um einen Hinweis darauf zu geben, wo man mit dem Lesen beginnen soll.
<div class="story-circle">
<p><strong>Hello there!</strong></p>
<p>This is</p>
<p>quite an</p>
<p>interesting way</p>
<p>to tell a</p>
<p>story</p>
<p>all around in</p>
<p>a circle.</p>
<p>What fun!</p>
</div>
CSS
Wir beginnen das CSS, indem wir eine height von 100% auf das :root-Element setzen.
:root {
height: 100%;
}
Als nächstes setzen wir mehrere Eigenschaften auf das <body>-Element:
- Wir beginnen mit der Festlegung einer
heightvoninherit, was bedeutet, dass das<body>die100%-Höhe des:root-Elements erben wird und daher die volle Höhe des Ansichtsfensters überspannt. - Als nächstes zentrieren wir das
<body>horizontal mitmarginund geben ihm einemax-width. Wie später zu sehen ist, ist diese obere Grenze wichtig für die Steuerung der maximalen Rotation der Fächer-/Kreisform. - Wir zentrieren den
<div>mitclass="story-circle"horizontal und vertikal innerhalb des<body>mit flexbox. - Wir verwenden die
container-type-Eigenschaft, um das<body>als ein Inline-GröÃenabfrage-Container zu deklarieren. Das ist wichtig, weil wir die Formrotation basierend auf der<body>-Breite variieren wollen und nicht auf der Ansichtsfensterbreite wie in den vorherigen Beispielen. Die Einstellung als GröÃenabfrage-Container ermöglicht es uns, seine GröÃe in Berechnungen zu referenzieren.
body {
height: inherit;
margin: 0 auto;
max-width: 1600px;
display: flex;
align-items: center;
justify-content: center;
container-type: inline-size;
}
Jetzt kommen wir zur Gestaltung des story-circle-<div>. Wir setzen seine width und height auf 1px: Es wird als Bezugspunkt fungieren, um den seine Kinderabsätze in einem Kreis positioniert werden. (Wir müssen nicht einmal Positionen darauf festlegen, da es in Ordnung ist, wenn die Absätze relativ zum <body> positioniert werden).
Wir erstellen dann eine Benutzereigenschaft namens --width-percentage, die das Ergebnis von 100cqw (100% der Breite des Abfragecontainerelternteils des Elements, welches das <body>-Element ist) geteilt durch 1200px, minus 0.33333, enthält. Dies ist der zentrale Wert, der die Menge regelt, um die sich der Kreis dreht, wenn sich die Breite des Ansichtsfensters ändert.
.story-circle {
width: 1px;
height: 1px;
--width-percentage: calc((100cqw / 1200px) - 0.33333);
}
Wir verwenden die Container-Abfragebreite anstelle der Ansichtsfensterbreite zur Steuerung der Rotation, weil wir eine maximale Grenze für den Container setzen wollen, die durch die max-width des <body> gesteuert wird. Wenn der Rotationswert gröÃer wird als ein Vollkreis, beginnt der letzte Absatz, sich mit dem ersten zu überlappen, was den Effekt verdirbt.
Da die max-width 1600px beträgt, hätten Sie vielleicht erwartet, dass die Berechnung 100cqw / 1600px) lautet. Das würde funktionieren, aber wir haben stattdessen (100cqw / 1200px) - 0.33333 gewählt (das 0.33333 stammt von 1600px/1200px - 1). Beides würde dazu führen, dass die maximale Rotation bei einer <body>-Breite von 1600px auftritt, aber der Fächer hat nun eine kleinere minimale Rotation, was bei schmalen Ansichtsfenstern einen besseren Effekt ergibt.
Die letzte Stilregel wählt die Absätze selbst aus. Das meiste davon ist grundlegende Stilgebung. Es sei erwähnt, dass wir position auf absolute gesetzt haben, damit alle Absätze übereinander liegen, und dass wir einen transform-origin-Wert von center left festgelegt haben, damit die Absätze sich um die Mitte ihrer linken Kante drehen, sodass sie sich alle in einem Mittelpunkt über ihrem umschlieÃenden <div> ausbreiten.
Nun zum interessanten Teil â wir definieren eine Benutzereigenschaft namens --angle, die den einheitenlosen Rotationswinkel des Absatzes enthält, bevor wir die rotate-Eigenschaft auf den resultierenden Wert setzen. Wir multiplizieren den resultierenden Betrag mit 1deg, um ihn in einen Gradwert umzuwandeln. Die --angle-Benutzereigenschaft ist das Produkt von drei Werten:
- Dem
sibling-index()des Absatzes minus1, wodurch der erste Absatz einen Rotationswinkel von0hat, da wir ihn horizontal haben möchten. 360geteilt durch diesibling-count()des Absatzes, wodurch alle Absätze gleichmäÃig um den Kreis herum verteilten sind, was bedeutet, dass das Design auch dann funktioniert, wenn sich die Anzahl der Absätze ändert.- unsere
--width-percentage-Benutzereigenschaft, die bewirkt, dass die Rotation der Absätze um den Kreis variiert, wenn sich die Ansichtsfensterbreite ändert. Denken Sie daran, dass diese Eigenschaft einen Maximalwert von1hat, der erreicht wird, wenn das<body>-Element seinemax-widthvon1600pxerreicht.
p {
padding: 10px;
width: 150px;
text-align: right;
background: linear-gradient(to right, red, orange 50%);
border-radius: 5px;
position: absolute;
transform-origin: center left;
--angle: calc(
((sibling-index() - 1) * (360 / sibling-count())) * var(--width-percentage)
);
rotate: calc(var(--angle) * 1deg);
}
Ergebnis
Um das Ergebnis zu sehen, sehen Sie sich unser Live-Beispiel für den animierten Geschichtenkreis an (Quellcode ansehen). Versuchen Sie, die Breite des Ansichtsfensters zu verändern, um zu sehen, wie sich die fächerförmige Anordnung der Absätze in einen Kreis entfaltet und zurück.
Siehe auch
calc(),abs()- CSS Werte und Einheiten Modul
- CSS Typisierte Arithmetik auf css-tricks.com (2025)