The hypot() function takes a list of values and returns the square root of the sum of their squares.

.hypotenuse {
  width: hypot(30vmin, 40vmin); /* 50vmin */
}

Most of the time, we’ll pass it two arguments: hypot(A, B). Think of it like a way to complete the Pythagorean theorem where we give the function the opposite and adjacent sides of a triangle, and the longest side is what it returns.

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

Syntax

hypot(<calc-sum>#)

The hypot() functions takes a comma-separated list of calculations (<calc-sum>) that must resolve to a <number><dimension>, or <percentage>.

Arguments

/* Takes dimensions */
width: hypot(30px, 40px); /* 50px */
width: hypot(12rem, 160px)

/* Takes percentages  */
width: hypot(60%, 80%);
width: hypot(30px, 10%)

/* Takes numbers */
hypot(9, 12) /* 15 */
hypot(15, 20) /* 25 */

/* Takes negative values */
width: hypot(-50px, 120px); /* 130px */
width: hypot(-90%, -120%); /* 150% */
width: hypot(9, 40); /* 41 */

<calc-sum> is a comma-separated list of calculations that resolve to either a <number><dimension>, or <percentage>.

Mixing <dimension> and <percentage> values is fine as long as they have a consistent type — like 25% and 5rem in the width property, or else the function is invalid.

Lastly, the result of hypot() will be of the same type as its arguments, so hypot(<number>, <number>) returns a <number>, and hypot(<dimension>, <dimension>) returns a <dimension>.

Three ways to see it

There are three main ways we can think of hypot():

1. While boring, we can think of hypot() as just its underlying formula, which squares each argument, sums it, and takes the square root of the result. You know, just like you learned in school: “A squared, plus B squared equals C squared.

This explains why it can take negative values, since they get squared into a positive value.

hypot(a1,a2,,an)=a12+a22++an2

2. However, hypot() has a whole lot more interpretation beyond a formula. If given two values, hypot(A, B) returns the length of the hypotenuse of a right triangle with sides A and B.

Instead of the sides of a triangle, we can also think of both arguments as the coordinates of a point on an XY plane. In which case, hypot(x, y) returns the length from the origin to that point in space.

3. That said, hypot() can take more than two arguments, so we’ll extend the last definition beyond two dimensions where something like hypot(a1, a2, ..., an) is the length of a point to its origin in an n-dimensional space. For example, seen in three dimensions, hypot(x, y, z) would look like this:

We’ll look at basic usage with two values to help illustrate how we’d put this into practice. Plus, CSS isn’t too keen on higher dimensions beyond 2D.

Basic Usage

Let’s start with a not-so-practical example for hypot(), just to showcase how it works. Imagine we want to cast a line from one point on the screen to our mouse position in the viewport.

The first step is to let CSS know the mouse’s x and y coordinates through the next JavaScript snippet, which saves each coordinate in the --m-x and --m-y variables:

window.addEventListener("pointermove", (event) => {
  let x = event.clientX;
  let y = event.clientY;

  document.documentElement.style.setProperty("--m-x", `${Math.round(x)}px`);
  document.documentElement.style.setProperty("--m-y", `${Math.round(y)}px`);
});

From here, we need to answer two questions: (1) How long should that line be? (2) what angle should we rotate it?

The first question is where hypot() comes in, since the length of that line is the same as hypot(x, y).

.line {
  /* Places line at the top left corner */
  position: fixed;
  top: 0;
  left: 0;

  width: hypot(var(--m-x), var(--m-y));
  height: 5px; /* Thickness of the line */
}

With this, we have a line that gets longer as the mouse moves away from the top-left corner.

Now, we have to rotate it by a certain angle, so we’ll use the atan2() function. Without going too much into details, atan2(y, x) gives us the angle between a point in the plane and the horizontal axis.

.line {
  /* ... */

  transform-origin: left center;
  transform: rotate(atan2(var(--m-y), var(--m-x)));
}

Note that we pass first y and then x into atan2() rather than the other way around.

And now the line should perfectly connect to our mouse.

If we want to instead connect the line from the center to the mouse, we would have to first place our line at the center:

.line {
  position: fixed;
  top: 50%;
  left: 50%;

  /* ... */
}

Then we tweak our JavaScript a bit so that our coordinates also start from the center instead of the top-left corner. This can be done by subtracting half the window’s innerWidth and innerHeight from the respective coordinate before passing them onto CSS.

x = x - window.innerWidth / 2;
y = y - window.innerHeight / 2;

Detecting When the Cursor is Near

So, now that we know the basics of how hypot() works, we can use it for a more practical situation. Not so long ago, Daniel Schwarz wrote about :near(), a potential new pseudo-selector that would match an element when the cursor is close to it by a given distance:

button:near(3rem) {
  /* Pointer is within 3rem of the button */
}

This could be used to apply some effect to a button whenever the user is near it, like highlighting or scaling it a bit. Unfortunately, at the time of writing, we don’t have :near() on any browser, but we can already simulate it using both hypot() and style queries.

Again, our first step is to pass the mouse coordinates to CSS via JavaScript. However, this time we’ll measure the mouse position from the center of the button. To do this, we’ll:

  1. Subtract from each coordinate the button’s offsetLeft and offsetTop, which moves the origin to the button’s top-left corner.
  2. Subtract half the button’s clientWidth and clientHeight, which moves the origin right to the button’s center.
const button = document.querySelector("button");

window.addEventListener("pointermove", (event) => {
  let x = event.clientX;
  let y = event.clientY;

  x = x - button.offsetLeft - button.clientWidth / 2;
  y = y - button.offsetTop - button.clientHeight / 2;

  document.documentElement.style.setProperty("--m-x", `${Math.round(x)}px`);
  document.documentElement.style.setProperty("--m-y", `${Math.round(y)}px`);
});

Back to CSS! Let’s define two variables: (1) --near that holds the distance from the mouse to the button, and (2) --limit that defines the limit where we consider the mouse near the button.

:root {
  --near: hypot(var(--m-x), var(--m-y));
  --limit: 200px;
}

What’s left is to create a style query that matches whenever --near is smaller than --limit, meaning our cursor is within the limit.

@container style(--near < var(--limit)) {
  button {
    scale: 1.025;
    box-shadow: #e07a5f 0px 6px 25px 0px;
  }
}

Before we look at the result, it’s worth checking ion your browser supports container style queries.

It’s More Than Syntactical Sugar

Since hypot() returns the square root of a sum of squares, the biggest math nerds of us would expect that the following two expressions would be equivalent:

hypot(A, B)
sqrt(pow(A, 2) + pow(B, 2))

However, if we try to switch the former for the latter, we’ll notice that it won’t work most times. The two formulas aren’t interchangeable whenever hypot() takes either a <length> or <percentage>, since all other exponential functions only accept <number> values. In other words, we need hypot() if we are working with specific types of values, notably <length> and <percentage.

The main reason behind this is clashing expectations. As the spec says, if in our stylesheet 1rem equals the default 16px, pow(1rem, 2) would result in 1rem (16px again), even though they are the same value. Meanwhile, pow(16px, 2) results in 256px. hypot() inputs and outputs are always consistent, so these kinds of unexpected results don’t occur!

Note: The spec goes into a longer explanation on why hypot() takes both dimensions and numbers.

Edge Cases

  • If any of the arguments is infinity or -infinity, then the result is infinity.
  • If, for any reason, you want to input only one value, it will return that same value as a positive.

Specification

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

Browser support


hypot() originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *