Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
Like Ray, your old buddy from the neighborhood, the CSS ray() function points you toward anime treasures… well… animated treasures… well… offset treasures… Okay, maybe it’s not like your friend Ray after all.
The ray() function is used in combination with the offset-path property and creates a line segment, a ray. It lets you move or place an element using polar coordinates, i.e. coordinates based on a distance and an angle instead of Cartesian coordinates, where the X coordinate is on the horizontal axis and the Y coordinate is on the vertical axis.
.element {
offset-path: ray(180deg farthest-side contain at 100px 200px);
}
This time traveling clock uses ray() for everything except the the outer circle:
What is a ray?
One of the difficulties with discussing rays is that they have no definitive visual component. And, unlike with shapes, we can’t even use clip-path to make them visible because a line has no width. I have thus included a thin gray line in the demos that follow to approximate the ray so you have a visual reference. The ray does not appear by default, just to be super clear.

A ray is simply a line, one that extends from its starting point at the center of the containing block (or parent element) by default in the direction you choose based on whatever angle you set for the distance between the starting point and the closest side of the containing block.
Note: In most cases, the containing block is the content section of the closest block level ancestor. There are a few exceptions to this, but that’s beyond the scope of this article.
Syntax
ray() = ray( <angle> && <ray-size>? && contain? && [at <position>]? )
<ray-size> = closest-side | closest-corner | farthest-side | farthest-corner | sides
Basically, this means that the CSS ray() function can take up to four arguments (well, five if you count X and Y coordinates separately), including:
- An
<angle> - The length of the ray, or
<ray-size> - The
containkeyword - The
atkeyword with a<position>that represents rays starting point as X and Y coordinates (the default position is the containing element’s center point)
Arguments
Let’s break those down, one by one, though slightly out of order to help explain things linearly… you know like in a line… or ray… get it? No? You see, because a ray is a line and… Oh, I see. You were just messing with me. Got it.
<angle>
This is the only required argument of the bunch. It can be set in degrees, radians, gradians or turns. Zero degrees (or radians or gradians or turns) points toward the top of the screen. And proceeds to the right as units increase (i.e., clockwise).
at <position>
Despite being fourth on our list above and coming last in the list of arguments, it would be prudent to talk about the starting point before we get into anything else. To set a starting point you use the at keyword followed by the x and y value of where you want the starting point to be (and, yes, the irony of using Cartesian coordinates to set a starting point for our polar coordinate system is not lost on me.) If you don’t set a starting point, the starting point will default to anything you’ve set with offset-position, and if you haven’t set an offset-position, the starting point defaults to the center point of the containing block.
<ray-size>
The length of the ray is determined by keyword and does not take any kind of length unit. No percentages or anything else, just the keywords: closest-side, closest-corner, farthest-side, farthest-corner and sides.
closest-side: This is the distance from the starting point to the closest side of the containing block. This is also the default so you can skip adding this keyword if it’s what you want. Also if the starting point is on a side of the containing block, the length of the ray will be 0 (because that’s automatically the closest side).closest-corner: The distance between the starting point and — you guessed it — the corner closest to the starting point. Similar to the above if the starting point lies on a corner of the containing block, the ray’s length will be0.farthest-side: You’re probably good at guessing what this means by now, but for those of you not great at patterns, this sets the length as that from the starting point to the side of the containing block farthest away from the starting point.farthest-corner: Bet you can’t guess how long this one is! The distance from the starting point to the corner farthest from the starting point, you say? Well, you’re wrong! Wait, I’m being told I’m not allowed to “just make things up” or “otherwise negate the truth because I feel like it.” Ugh… You’re no fun… Fine… Reader, you were right. It is the distance from the starting point to the farthest corner.
For all of the above keywords the only things that determine the length of the ray are the starting point and the side or corner you set with a keyword. Lets look at an example.
In the above, the containing block is 200px by 300px and the starting point is placed at 195px, 295px. The length is set to farthest-side which makes the ray 295 pixels long as the top will be 295 pixels away. If you then set your angle to 90 degrees, the ray will start at 195px, 295px and continue for 295 pixels along the horizontal X axis until it is 290 pixels past the containing block’s edge. So, even though the distance is determined by a metaphorical line drawn from one point to a side or corner, where that ray ends up can be wildly different depending on the angle used, but the angle itself plays no role in determining the length of the ray. I chose 90 degrees above for ease of demonstration. Whatever angle you choose the ray will still be 295 pixels long.
The sides keyword works differently than those above.
sides: This keyword sets the ray’s length as the distance from the starting point to the first internal side it comes across. The angle then, can very much determine the length of the ray. From the example above, if the angle were 90 degrees and the length set to sides, then the ray would be 5 pixels long because the first internal side it would hit would be the right side.
If the starting point is outside the containing block, the length of the ray is zero. According to the spec it should also be 0 if the starting point is on the edge of the containing block, but I didn’t find that to be strictly true, or, at least, not in the way I expected it to be. I expected the ray to have zero length if it was on the edge at all, however, it would only have zero length if the angle pointed outside the box. If the angle pointed inside the box or along the edge it would work just fine.
contain
The contain keyword “contains” the ray within the containing block, but it does not stop the ray from going outside the containing block. That doesn’t make sense you say? You’re not wrong. But to explain what it does do, let’s step back to a simple example.
We have a box that’s 200 pixels wide and high. Let’s add an element that’s 50 pixels wide and high with 50% border radii (we’ll call it the ball). Then we set a ray on it:
offset-path: ray(0deg farthest-side at 100px 200px);
This will place a vertical ray at the horizontal center of the box. If I then animate the ball on that ray the ball’s center point will travel from the starting point (100px, 200px) to 100px, 0px. Meaning that half of the ball will be outside the containing block on each end of the ray. If we then add the contain keyword to that ray:
offset-path: ray(0deg farthest-side contain at 100px 200px);
…the ray’s length will be decreased so that the ball, at 100% offset distance won’t go past the containing block. The ray will be reduced by half the width or half the height whichever is larger (and the ray won’t be less than zero.)
I mentioned that it goes outside the containing block, but this seems like it stays very much inside the containing block. Well, again you’re right, but that’s because I carefully crafted the example. If we change the angle of the ray from 0 degrees to 180 degrees, you’ll see what I mean:
offset-path: ray(180deg farthest-side contain at 100px 200px);
The ray will be exactly the same length as when it was at 0 degrees (because farthest-side doesn’t take the angle into account when determining the length of the ray), but will go from 100px, 200px to 100px, 375px, staying outside the containing block almost the entire time.
Demo
Play around with the different arguments and get to know your old buddy, Ray.
Specification
The ray() function is defined in the Motion Path Module Level 1 specification, which is currently in Working Draft status. That means the information can change between now and when it goes through the formal editing and recommendation process.
Browser support
The ray() function was included in Interop 2024, which means it is widely available and supported by all major browsers.