The CSS tan() trigonometric function takes a value and returns its tangent. Specifically, it takes a calculation that resolves to either an <angle> or <number> and returns the result’s tangent, which ranges between -infinity and infinity. The tan() function is perfect when working with triangle lengths.
For example, we can take a series of dots and move them around in squiggly line by arranging them vertically along the tangent of a specific angle multiplied by a certain height:
.ball {
transform: translateY(calc(tan(15deg * var(--i)) * 5dvh));
}
Each dot is moved by a certain amount based on its order:
The tan() function is defined in the CSS Values and Units Module Level 4 specification.
tan( <calc-sum> )
…where <calc-sum> is either an <angle> unit (e.g. 45deg) or a <number> unit (e.g. 50px). 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 tan() of that result.
/* using angles */
height: calc(tan(60deg) * 50px);
height: calc(tan(0.2turn) * 50px);
/* using numbers (radians) */
height: calc(tan(1.5) * 50px);
height: calc(tan(0.5) * 50px);
/* using symbols (pi, e) */
height: calc(tan(pi / 3) * 50px);
height: calc(tan(-1 * e) * 50px);
/* inner calculations */
height: calc(tan(max(30deg, 0.2turn)) * 50px);
tan()?When graphed, the tan() function looks like a line that goes from the bottom of the graph (to negative infinity) upwards (to positive infinity) with a slight curve in the middle.

Yes, this graph represents all the values tan() can take, but it doesn’t really tell us why they are there. For that, we’ll have to look at one (of the many) definitions of tan() using a right-angled triangle.
As its name notes, a right-angled triangle is a triangle with a 90-degree angle.

If we pick one of the angles (in this case, the bottom-right one), we’d have three sides:
Then, the tangent of that angle gives us the ratio between the adjacent and opposite sides:

So, as the opposite site increases, tan() does too. But then it decreases as the adjacent site increases:
This explanation gives tan() a grounded definition, but we can also give it a more “formal” one: the tan() of an angle is the quotient between the sin() and cos() of that same angle:

Using tan(), we can create a perfect triangle given an angle and either its height or width. In other words, if we want a triangle of a given height and angle, we can calculate its corresponding width using tan(), and vice versa.
For example, if we say that the width of a triangle is its adjacent side, and its height is the opposite side, then we have the following relations:

Which translates to CSS like this:
.fixed-width {
width: var(--t-width);
height: calc(var(--t-width) * tan(var(--angle)));
}
.fixed-height {
height: var(--t-height);
width: calc(var(--t-height) / tan(var(--angle)));
}
To avoid using a custom property for the fixed dimension, notice that the aspect ratio for each case is given by the following formula:
aspect-ratio: calc(1 / tan(var(--angle)));
So… how can we use this in our day-to-day work? Let’s start with the following setup: an unordered list of phrases stacked vertically where each phrase has a different background color:
Notice that 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: 6). 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.
Each phrase is a little longer than the last one, so we might be tempted to make their widths follow that natural length, but just setting each element’s width to the phrase’s length would make the whole list look a little clunky. Instead, we can make each element’s width grow by a certain angle so they stack like a staircase.
The tan() function is perfect for this! All of the elements have a fixed height, so we can calculate their width like this:
li {
aspect-ratio: calc(var(--i) / tan(45deg));
}
Behold! The phrases stack like a staircase, where the top step is shortest and each one after gets a little wider forming the staircase:
Giving it a little more swag with animations, we can stagger the width:
You can probably imagine how interesting this sort ofd thing would be for interactive bar charts!
At the beginning, we said that the tan() function can return values between negative infinity (-Infinity) and positive infinity (Infinity), but so far, we’ve only used tan() to return positive values, and they weren’t that big. So, where exactly do all these values come from?
To answer this, we’ll have to extend the initial definition that tan(θ) is equal to sin(θ)/cos(θ). Remember that sin() and cos() are defined in the unit circle, so we’ll also define tan() there.
If we have an angle, then we can cast a line (let’s call it L) from the center, and it will land somewhere in the unit circle. From there, we can draw another line perpendicular to L that goes from the point on the unit circle to the x-axis.
The length of this line is given by tan()! And looks like this:
After playing around with the angle, you’ll notice two things:
tan() is only positive in the top-right and bottom-left quadrants. You can see why if you look at the values of cos() and sin() there.tan() is undefined on 90° and 270°. What do we mean it’s undefined? Well, this angle give us a parallel line to the x-axis, and they won’t intersect ever, so it’s infinitely long. However, it’s undefined since it could be infinitely large to the right (positive) or left (negative), because it can be both, we say it isn’t defined. Since we don’t have “undefined” in CSS in a mathematical sense, it should return an unreasonably large number, depending on the case.The tan() 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 it formally becomes a candidate recommendation for browsers to implement.
.element { rotate: acos(0.5) }
.element { rotate: asin(-0.5); }
.element { rotate: atan(1); }
.element { rotate: atan2(200px, 200px); }
.element { transform: translateY(calc(cos(20deg * var(--i)) * 100px)); }
.element { transform: translateY(calc(sin(20deg * var(--i)) * 100px)); }