atan()

Juan Diego Rodríguez on

The atan() trigonometric function takes a number and returns its inverse for the tan() function. Specifically, it takes any number between -Infinity to Infinity and returns an angle between -90deg and 90deg (in radians) that, when passed to tan(), produces that number.

.element {
  --atan: calc(atan(var(--i) / var(--count)));

  /* atan() returns an angle, so to graph it, I used it as a number  */
  transform: translateY(calc(-1.5px * var(--int-atan)));
}

This is unlike acos() or asin(), whose input is limited between -1 and 1.

The atan() function is defined in the CSS Values and Units Module Level 4 specification.

Syntax

atan( <calc-sum> )

The atan() function takes in any calculation that must resolve to a number; otherwise, it makes the declaration invalid.

Arguments

/* it undoes tan() */
rotate: atan(1.73); /* ~60deg */
rotate: atan(1); /* 45deg */
rotate: atan(atan(30deg)); /* 30deg */

/* it can take any number between -Infinity and Infinity */
rotate: atan(Infinity); /* 90deg*/
rotate: atan(100000); /* ~90deg*/
rotate: atan(-Infinity); /* -90deg*/

/* we can do inner calculations */

rotate: atan(sqrt(3)); /* 60deg */
rotate: atan(sqrt(3) / 3); /* 30deg */

Basic usage

Before checking what atan() represents, I think it’s worth getting a little familiarized with it. As I mentioned at the beginning, atan() takes any number and returns a specific angle. If we want to target some notable angles (like 30° or 45°), then we’ll need to input the following numbers:

.square {
  rotate: atan(-Infinity); /* -90deg */

  rotate: atan(0); /* 0deg */
  rotate: atan(2 - sqrt(3)); /* 15deg */
  rotate: atan(sqrt(3) / 3); /* 30deg */
  rotate: atan(1); /* 45deg */
  rotate: atan(sqrt(3)); /* 60deg */
  rotate: atan(2 + sqrt(3)); /* 75deg */

  rotate: atan(Infinity); /* 90deg */
}

But where do these relationships between numbers and angles come from?

What’s atan()?

To understand atan(), we have to talk first about its original function, tan(), which takes an angle and returns its tangent as a number. And to define what exactly this tangent represents, we’ll have to look at one of its definitions through the right-angled triangle, which, as its name implies, is a triangle with a 90° angle.

Diagram of a right triangle pointing towards the right. The sides are labeled opposite, adjacent, and hypotenuse relative to the angle.

If we pick one of the other two angles, we’d have three sides:

  • Adjacent (the one touching the angle)
  • Opposite (the one away from the angle)
  • Hypotenuse (the longest side)

Then, given an angle, tan() returns the ratio between the opposite and adjacent sides.

The tangent of theta is equal to the opposite side divided by the adjacent side.

What if we knew the length of the triangle’s sides and wanted the angle instead? Then, it’s as easy as using atan() to find it!

Angle equals the atan multiplier by the opposite side divided by the adjacent side.

From here, we can explain some notable angles. For example, -Infinity and Infinity return -90deg and 90deg respectively, since for one of the angles to be 90deg, we would need an infinitely long side. Or if both sides are of equal length, their quotient would be 1, so they would have a 45deg angle.

Finding the perfect angle

All this theory is fine, but how can we use atan() in a practical way? Well, uses aren’t scarce at all: every time we want to find an angle given two sides of a (right) triangle, we’ll need to use atan()! And they appear in a lot of places.

Imagine we wanted to section a page diagonally, like the following layout:

An image element split divided by a diagonal that stretches from the bottom-left to the top-right, coloring half of the background in a solid black with white text on top of it.

…and we started with the following markup:

<main>
  <h1>Meet Lorem, ipsum.</h1>
  <p>
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima quod architecto suscipit! Lorem, ipsum dolor sit
    amet consectetur adipisicing elit.
  </p>
  <p>
    Cupiditate ratione magni dicta qui natus reiciendis totam quidem ex. Accusantium nisi vero ratione ea doloremque
    quis, consectetur aperiam eum!
  </p>
</main>

…along with a little styling for the background-image:

body {
  background-image: url("https://picsum.photos/id/32/1080/1080/");
  background-size: cover;
  background-position: bottom left;
}

So, right now, we only have our content behind a background image:

Gnarly, right? The first thing we would like to do is section the page diagonally into two halves. We can do this using a conic-gradient() placed at the bottom left corner of the page that crosses half the page:

main {
  height: 100vh;
  background: conic-gradient(at bottom left, oklch(0.2 0 0) 45deg, #0006 calc(var(--atan) + 2deg));
}

As you can see, we sectioned the page by a 45deg angle, but this will only work if the page is a perfect square. If we want to find the perfect angle, we’ll need to use atan(). To do so, we’ll think of the page as a big right triangle, where its height is the adjacent side and its width the opposite.

A rectangular shape bisected by a diagonal line going from the bottom left to the top right, forking two right triangles.

Then, replacing those values into the atan() formula, we’ll find our desired angle:

:root {
  --angle: atan(100vw/100vh);
}

And we can just plug it into our conic-gradient()!

main {
  height: 100vh;
  background: conic-gradient(at bottom left, #161616 var(--angle), #0006 calc(var(--angle) + 2deg));
}

As you can see, the content overflows outside the upper section we just made. To fix this, we’ll need to create a safebox inside the triangle where we make sure the content is kept.

The text against the black background color is displayed with a dashed white border around it, indicating the safe space we do not want to overflow.

It’s simpler than we might expect since we already did half the work! Let’s say we want to make the safe box’s height one half of the page’s height. This means we would want its width to extend until it touches the triangle’s border. If we draw how this might look, we’ll notice another right triangle popping up.

We have all the necessary data to find the width! Substituting the values into tan()‘s formula, we can solve for it:

The width of the safe box is equal to the tangent of the unknown angle multiples by 100% of the viewport height minus the safe box height.

The only thing to note is, in order to create the safe box, we’ll use the element’s padding. So, we’ll subtract the value we just got for the width from 100dvw to get the correct padding:

/* In case you haven't set it */
* {
  box-sizing: border-box;
}

main {
  /* ... */
  --sb-height: 50vh;
  padding-bottom: calc(100vh - var(--sb-height));
  padding-right: calc(100vw - ((100dvh - var(--sb-height)) * tan(var(--angle))));
}

What’s left is to add some padding on the other sides to give it a little breathing space:

main {
  /* ... */
  padding-top: 3rem;
  padding-left: 3rem;
}

And we are done!

Specification

The atan() function is defined in the CSS Values and Units Module Level 4 specification, which is currently in Editor’s Draft.

Browser support

More information