DEV Community

The-architect
The-architect

Posted on

A Learner's Exploration of CSS Translate

case study: Agba Akin’s CSS clock.

Link to reference:

A Beautiful wall clock made with only Neumorphism CSS - Akinola Abdulakeem

I came across this project on Twitter and had made a note to replicate it but when I came around to it, I realized just reading the code and replicating it won’t satisfy me. I wanted to understand the engine beneath the hood - and that led to this article.

In this article and the others that will follow, I will try to understand the thought and build of this nice project by shedding particular focus on the CSS concepts it applies to achieve its make and functionality. The goal is not just to be able to build one clock, but to understand the concepts and their applications so as to be able to build several other clocks, as well as other interesting things.

Being a learner, the animation was mighty cool but The first challenge that really got my attention was the arrangement of the numbers - especially after seeing the HTML. So here’s to understanding the translate() function of the CSS transform property.

The translate() function can be passed as a value to the CSS transform property. The function repositions an element in the horizontal and vertical direction.

MDN docs describes it as being defined by a two dimensional vector. This simply means it is defined along two directions. It uses the graph X and Y axis to compute the movement direction of the element it is applied on.

The two values the function takes as parameters act as coordinates to determine how the element will move and be located on the horizontal (x) and vertical (y) axis.

Code snippet showing a CSS transform declaration

For example, The div with class “inside” will be translated 20px on the horizontal axis and 10px on the vertical axis. Without any prior positioning, the default origins as you may have assumed is 0,0 but unlike in our graphs from math class, here, we start from the top right corner of the container.

Before any transformation is applied on the element

Element before any transformation is applied

And after the code snippet above is applied

Element after transformation is applied

While the code snippet above uses two parameters, the translate() function can also take only a single value. The value parameters have to be a length or percentage value.

When a single value is passed to the translate() function, it uses it to compute only the movement on the horizontal axis while the vertical (Y) axis is defaulted to 0.

Code snippet showing translate function with only one parameter
transform: translate(50px) and

transform: translate(50px, 0px) will yield the same result:

Resulting effect of single parameter translate() on element

When the translate function is passed a percentage value, the percentage will relate to the width of the element being transformed. Say we pass a value of 50% to our translate function, the positioned element will be translated a distance equal to 50% of its own width.

To demonstrate this;

Code snippet showing translate function passed with single percentage parameter

this code snippet will result in this:

Element moves by 50% its own width

If we add another Div of the exact same size but without any transformation applied to it as a reference, and set the translate parameter of our first div to 100%, we see our translated div moves by exactly the width of the second div

Percentage translation demonstrated against a non transformed element

So whenever a percentage parameter value is passed to the translate() function on an element, the distance it computes is the percentage of its own total width.

When double parameter values are passed to the translate() function, they define the horizontal and vertical vector in that particular order - The first value translates the element on the horizontal (x) axis, while the second value moves the element on the vertical (Y) axis.

The values passed can both be length values, percentage values or a mixture of length and percentage values. When both values are percentage values, the first value relates to the width of the referenced element, while the second value refers to the height of the referenced element.

Code snippet with two percentage parameters passed to the translate function

The code snippet will move the div by a distance equal to 50% its own width on the horizontal axis, and a distance equal to 30% its own height on the vertical axis.

Image demonstrating translate effect of code snippet above

The width and height of our sample div is 15em (240px) and so its translated by 50% * 15em on the X-axis, and 30% * 15em on the Y-axis.

Note that the translate() function positions elements without altering their position in the normal document flow.

Now to the actual arrangement of the numbers.

Knowing how the translate function works, all that’s left is determining the values to define our position coordinates. The case study example first brings all the numbers to the center of the clock and then uses translate() to position them around the invisible round edge. The declarations of the code block that brings the numbers to the center is shown below:

Code snippet showing declaration to center numbers within container

The (...) represents all the other code contained within this block. for the purpose of clarity and simplicity, I’m only sticking to what’s necessary four our current focus.

If the elements are not exactly centered by the code snippet above, make sure to check that the elements margins are reset to zero. The offset defined by default margins from the element styles can throw the elements off.

Next is positioning the numbers around the center point to appear like they radiate from the point. The translate() function comes in here but to get the parameter values that will serve as coordinates, we have to do a little geometry first.

The numbers are arranged around a circular path and their positions are points that can be mapped relative to the origin of the circle, its radius, and angle of each segment as represented below.

Segmented circle showing number positions along circumference

These points are used to determine the vertical and horizontal distances each number needs to be translated to.

Coordinates mapped from the relationship between the circle origin, radius and angle of rotation

As is probably obvious from the above figure, the height and widths of the colored rectangles results in the X and Y values for each number’s position. 12 and 3 have 100% movement on the Y-axis and 0 movement on the X-axis and 100% movement on the X-axis and 0 movement on the Y-axis respectively.

the transform declaration becomes:

code snippet of template transform declaration

where

n = the occurring order of the child element within its container,

x = distance translated on the x-axis

y = distance translated on the y-axis

The ‘:nth-child(n) syntax’ is a pseudo selector that points to the number n child of a parent element

I used a circle of 345px in diameter and the coordinate values are as follows:

code snippet of transform declaration using position data from circle geometry

All that is needed now is to substitute these values with the translate() parameters accordingly. Note that I’ve only done one quadrant of the circle. This is because these values will be same for all the other quadrants although there is a catch - that draws attention to the next point to note.

You may have noticed the counter intuitive use of the negative symbol, especially if you are the math-y type. This is because by default, the movement of the translate() function is top-bottom on the Y-axis and left-right on the X-axis. Since the movement of the numbers starts from the center of the circle, the default movement of the positive- y will be down and the positive- x will move left. This default movement is easily modified by using negative values where necessary just as has been done for the Y value of 12.

Positioning of the other numbers in the other quadrants is simply achieved by switching the negative values appropriately between x and y. For example, to position the number 6, my transform declaration will look like this:

Example of transform declaration with actual values substituted in

NB: The y-value may differ depending on the radius of the circle you use.

Further Explorations will try to understand some of the other CSS concepts that give the clock its functionality.

Top comments (1)

Collapse
 
jovialcore profile image
Chidiebere Chukwudi

✊🏾🔥🔥🔥