cos()

Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.

The cos() trigonometric function takes a value and returns its cosine. Specifically, it takes an <angle> or <number> and returns the result’s cosine, which is always a number between -1 and 1. It’s incredibly good for drawing waves or laying things out in waves.

.ball { transform: translateY(calc(cos(20deg * var(--i)) * 100px)); }

The cos() function is defined in the CSS Values and Units Module Level 4 specification. The sin() function is similar, but its result is a shift of cos(), so the two are closely related.

Syntax

cos( <calc-sum> )

…where <calc-sum> is either an <angle> unit (e.g. 45deg) or a <number> unit (e.g. 100px). But if you look at the next section, you’ll see that <calc-sum> can also be a nested calc()-ulation that returns an <angle> or a <number>, which is then calculated into the cos() of that result.

Arguments

/* Angles */ width: calc(cos(60deg) * 100px); width: calc(cos(0.2turn) * 100px); /* Numbers (expressed in radians) */ width: calc(cos(6.28) * 100px); width: calc(cos(1) * 100px); /* Mathematical symbols (pi, e) */ width: calc(cos(pi / 3) * 100px); width: calc(cos(e * 2) * 100px); /* Nested calc() within the function */ width: calc(cos(min(40deg, 0.125turn)) * 100px);

What’s cos()?

You’ll often see the cos() function graphed as a wave that goes up and down infinitely between -1 and 1, but where exactly do all those values come from? Well, to figure it out, we’ll need to look at one (of the many) definitions of cos(): the unit circle.

The unit circle is a circle of radius one that’s placed on the origin (center) of an X-Y coordinate system, meaning its right side has positive X-coordinates and its left side negative X-coordinates.

Unit circle centered around the midpoint of a X-Y graph.

We can describe each point of the unit circle by an angle that is measured from the positive side of the X-axis moving counter-clockwise.

However, most of the time we don’t describe points using angles, but by their X and Y coordinates. So, how can we switch between them? Well, cos() (and it’s inverse, sin()) do exactly that! Given an angle, the function returns the Y and X coordinate, respectively, in the unit circle.

From here, it’s easy to see why, when graphed, cos() returns a wave that goes from 1 to -1: it’s describing the X-coordinate as we go around the unit circle! This also means cos() is tightly related to the sin() function, since it describes the Y-coordinate instead.

Basic usage

Talking about circles, cos() allows us to create circular layouts in CSS without the headache of figuring out the exact points by hand. Let’s start with an unordered list of elements in HTML and set a flexbox layout on the container to put them all on the same line:

<ul style="--total: 8"> <li style="--i: 1">❤️</li> <li style="--i: 2">🧡</li> <li style="--i: 3">💛</li> <li style="--i: 4">💚</li> <li style="--i: 5">🩵</li> <li style="--i: 6">💙</li> <li style="--i: 7">💜</li> <li style="--i: 8">🩷</li> </ul>

You’ll notice each element has a hard-coded index (i.e., --i: 1, --i-2, etc.), and that we also set aside the total number of elements (--total: 8). This is necessary to place each element around a circle, but you may skip this step if your browser supports the sibling-index() and sibling-count() functions, which automatically calculate an element’s index and the total count of elements in a series.

Right now, the list items are placed in a boring straight line. If we want to place them around a circle, we’ll need to first position each element at the center.

li { position: absolute; top: calc(50% - var(--icon-size) / 2); left: calc(50% - var(--icon-size) / 2); }

Then, we’ll want to evenly space each element away from the center by a certain angle. To get that angle, we can divide 360deg (a full turn around the circle) by the total amount of elements, and then place each element by a multiple of that angle:

li { --offset: calc(360deg / var(--total) * var(--i)); /* alternatively */ --offset: calc(360deg / sibling-count() * sibling-index()); }

We’ll also need to define a certain radius to move each element away from the center:

ul { --radius: 10rem; }

Now each element has an angle around the circle and a radius from the center. What’s left is to use cos() (and sin()) to convert those values to X and Y coordinates, which, when multiplied by the radius, give us the values to translate each element by.

li { transform: translateX(calc(cos(var(--offset)) * var(--radius))) translateY(calc(sin(var(--offset)) * var(--radius))); }

Circular animations

We can animate circular motion in CSS, which can be used to create things like loading spinners or background animations. So, let’s pick up from the last example: a circular arrangement of items:

Let’s say we want to make the elements spin around the bigger circle. We have two options:

  1. Rotate the parent element (boring!)
  2. Rotate the individual elements

We’ll go with the second option since, as we’ll later see, it gives us more control for spinning things.

The items are already evenly placed around the circle, so we don’t want to mingle with their --offset. Instead, we’ll want to spin them by a new --rotation variable. We’ll need to register a custom property using the @property at-rule to start:

@property --rotation { syntax: "<angle>"; initial-value: 0deg; inherits: false; }

Now we can use our custom --rotation variable to calculate the angle that each item leans around the unit circle:

li { transform: translateX(calc(cos(var(--rotation) + var(--offset)) * var(--radius))) translateY(calc(sin(var(--rotation) + var(--offset)) * var(--radius))); }

Then, we define a set of @keyframes to animate the --rotation to complete a full turn, going from 0deg to 360deg:

@keyframes spinning { from { --rotation: 0deg; } to { --rotation: 360deg; } }

What’s left is to apply the animation to the items:

li { animation: spinning 4s linear infinite; }

Right now, each element spins linearly around the outer circle, but what if we wanted to add a little more sass to the rotation, like making each item wait a little at the top, make a full turn, and start again, maybe even adding a Bézier curve to the animation. Sort of like a Slinky, but around a circle.

First things first, we’ll set the --offset variable to -90deg, which is the top of the circle:

li { --offset: -90deg; }

Now each list item has the same --offset value, which means they are stacked on top of each other. To fix this, we’ll want to change its animation-delay based on its index (i.e., it’s order in the sequence):

li { animation-delay: calc(150ms * var(--i)); }

This works, but animation delays are only applied to the first iteration. To fix this, we can tweak the @keyframes animation to make it wait a little at the end of each iteration:

@keyframes spinning { 0% { --rotation: 0deg; } 70%, 100% { --rotation: 360deg; } }

What’s left is to get rid of linear animation and replace it with a cool Bézier curve:

li { animation-timing-function: cubic-bezier(0.65, 0.05, 0.36, 1); }

Demo

Specification

The cos() function is defined in the CSS Values and Units Module Level 4 specification, which is currently in Editor’s Draft. That means the information can change between now and when the feature formally becomes a Candidate Recommendation.

Browser support

More information