Iron Man’s Arc Reactor Using CSS3 Transforms and Animations

0
20

Alright Iron Man fans, fire up your code editors! We are going to make the Arc reactor from Iron Man’s suit in CSS. Here’s what the end result will look like:

See the Pen Iron Man’s Arc Reactor by Kunal Sarkar (@supersarkar) on CodePen.

The Full Page Wrapper

We will make our Arc reactor on a dark full-page background. Here’s our code to make a full page wrapper element:

body {
margin: 0;
}

.fullpage-wrapper {
height: 100vh;
background: radial-gradient(#353c44, #222931);
}

Why do we declare no margin on the body? The <body> element has some margin set to it by default in the user agent stylesheet. This prevents the elements inside the <body> to touch the edges of the screen. Since we want our wrapper to cover the entire screen, edge to edge, we removed that default margin on <body> element by setting it to 0.

We’ve given our .fullpage-wrapper the full height of the viewport. We don’t have to specify a width because a div is full width by default. We could have gone with another approach by setting both the width and height of the element to 100% but that comes with some possible drawbacks as more elements are added to the screen. Using viewport units ensures our wrapper always occupies the full vertical space of the screen, regardless of what it is or what other elements are added to the layout.

We also used a radial gradient on our wrapper using radial-gradient() CSS function. The parameters inside the function are the color start and end points. So, the center of the background will be more #353c44 and more #222931 towards the edges. It’s subtle, but a nice touch.

Centering the Reactor Container

Before we start creating our reactor, let’s create a container for it and center it:

.reactor-container {
width: 300px;
height: 300px;
margin: auto;
border: 1px dashed #888;
}

This gives us a 300px x 300px box with dashed border. The margin: auto; declaration ensures equal horizontal spacing.

But why it doesn’t center it vertically?

Per CSS2 specs, if we give auto margin to the left and right side, then the entire available space will be equally divided to left and right margin. This isn’t the same case for the top and bottom margin though. For top and bottom margin the CSS2 spec says:

If margin-top, or margin-bottom are auto, their used value is 0.

However, we do have a good news. The flexbox layout follows new rules of alignment, and here’s what the Flexbox spec has to say:

Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.

This means if the element in consideration is displayed as a flex item, then margin: auto; will work in both the directions. Let’s make our wrapper a flex container and its child elements flex items:

.fullpage-wrapper {
width: 100%;
height: 100vh;
background: radial-gradient(#353c44, #222931);
display: flex;
}

There, that’s much better:

There are many other methods to center elements in HTML. There’s a detailed guide on centering right here on CSS-Tricks to learn more.

The Reactor Core: Concentric Circles in CSS

We know that new elements in HTML are created from left to right (for left-to-right languages), or top to bottom. Elements never overlap, until you provide some negative margin.

So, how are we going to display concentric circles? We will use absolute positioning.

The default value of position property is static. Static and relative positioning follow the flow of top to bottom and left to right. However, an absolutely positioned element is not treated as a part of the document flow and can be positioned anywhere using the top, right, bottom and left properties.

Let’s start by creating the smallest circle:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– the smallest circle –>
<div class=”core-inner”></div>
</div>
</div>
.core-inner {
position: absolute;
width: 70px;
height: 70px;
border-radius: 50%;
border: 5px solid #1b4e5f;
background-color: #fff;
}

We need to center this div. You might be tempted to apply the same flexbox concept we used on the reactor element to center this circle as well. But, here’s what CSS2 spec has to say about setting margin: auto; on absolutely positioned elements:

If none of the three (top, height, and bottom) are auto: If both margin-top and margin-bottom are auto, solve the equation under the extra constraint that the two margins get equal values.

This means if an absolutely positioned element has any value for top, height and bottom other than auto, then setting the top and bottom margin to auto will center the element vertically.

Same case for horizontal centering: if an absolutely positioned element has any value for left, width and right other than auto, then setting the left and right margin to auto will center the element horizontally.

That means we don’t need flexbox layout to center an absolutely positioned element with a known height and width. We just have to make sure that we give some value to top, right, bottom and left other than auto. So, we will use 0:

.core-inner {
position: absolute;
width: 70px;
height: 70px;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
border-radius: 50%;
border: 5px solid #1b4e5f;
background-color: #fff;
}

We do not want to repeat these five lines for all the concentric circles we are going to have, so let’s create a separate class for this. We also don’t want to define border-radius: 50%; for all the divs that we want to display as circles, so we will create a class for that too:

.circle {
border-radius: 50%;
}

.abs-center {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;

}

.core-inner {
width: 70px;
height: 70px;
border: 5px solid #1b4e5f;
background-color: #fff;
}

Also, add these new classes to out .core-inner element:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– the smallest circle –>
<div class=”core-inner circle abs-center”></div>
</div>
</div>

Okay, our concentric circle is centered. Let’s make it glow.

But CSS doesn’t have any “glow” property.

Don’t worry, we have the box-shadow property. Instead of giving the shadow a dark color, we will give it a bright color to make the shadow look like glow. Pretty clever, isn’t it? 😉

Let’s do this:

.core-inner {
width: 70px;
height: 70px;
border: 5px solid #1b4e5f;
background-color: #fff;
box-shadow: 0px 0px 7px 5px #52fefe;
}

Let’s take a break and understand the syntax of box-shadow first because we will be using it a lot. Here are what those values for box-shadow mean in that order:

  • x-offset: how much we want to push the shadow on the right side (x-axis). Negative values will push the shadow to the left side.
  • y-offset: how much we want to push the shadow up or down (y-axis).
  • blur-radius: how blurry we want our shadow to be.
  • spread-radius: how much we want our shadow to spread.
  • color: color of the shadow.

Play with these values a bit to see their effects in real time.

In real life, shadows don’t drop only outside of an object. Shadows drop upon the objects too. Imagine a pit dug by a dog, it will have a shadow inside it, right?

We can give a shadow inside an element using the inset keyword in the box-sizing property. To give an element both, outside and inside shadow, we simply separate them with a comma. Let’s do this to get an outside and inside glow to our reactor’s inner core:

.core-inner {
width: 70px;
height: 70px;
border: 5px solid #1B4e5f;
background-color: #fff;
box-shadow: 0px 0px 7px 5px #52fefe, 0px 0px 10px 10px #52fefe inset;
}

Now it’s starting to look sci-fi!

Let’s create one more circle on top. We want the inner circle to display on top of the other circles, so we will add new circle divs *above* the inner-circle in code:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– the second circle –>
<div class=”core-outer circle abs-center”></div>
<!– the smallest circle –>
<div class=”core-inner circle abs-center”></div>
</div>
</div>

The elements down in the code, are displayed above on the screen. If we put the core-outer below the core-inner in the code, then core-inner won’t be visible, because core-outer will cover it.

Let’s give style to outer-code. The outer core will be a little bigger than the inner core and we will give an outer and inner glow to core-outer too:

.core-outer {
width: 120px;
height: 120px;
border: 1px solid #52fefe;
background-color: #fff;
box-shadow: 0px 0px 2px 1px #52fefe, 0px 0px 10px 5px #52fefe inset;
}

I want you to do one thing: look at the shadows (glow) and try to identify which one is of which circle. There are four shadows and two circles (until now).

To finish designing the core, we will need one more circle that will wrap the inner and outer circles:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– the third circle –>
<div class=”core-wrapper circle abs-center”></div>
<!– the second circle –>
<div class=”core-outer circle abs-center”></div>
<!– the smallest circle –>
<div class=”core-inner circle abs-center”></div>
</div>
</div>

This one will be a little bigger, and will again have same shadows, we will use a dark background for core-wrapper:

.core-wrapper {
width: 180px;
height: 180px;
background-color: #073c4b;
box-shadow: 0px 0px 5px 4px #52fefe, 0px 0px 6px 2px #52fefe inset;
}

Creating Reactor Coils and Rotating with CSS3 Transforms

We have the core of the reactor, now we need a tunnel around the core. Actually, we can create an illusion of a round tunnel by drawing just one more circle little bigger than the core-wrapper. Let’s do it:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– the largest circle –>
<div class=”tunnel circle abs-center”></div>
<!– the third circle –>
<div class=”core-wrapper circle abs-center”></div>
<!– the second circle –>
<div class=”core-outer circle abs-center”></div>
<!– the smallest circle –>
<div class=”core-inner circle abs-center”></div>
</div>
</div>

…a little wider and add same glow to the tunnel:

.tunnel {
width: 220px;
height: 220px;
background-color: #fff;
box-shadow: 0px 0px 5px 1px #52fefe, 0px 0px 5px 4px #52fefe inset;
}

Our tunnel is ready.

Make sure you do not just copy paste the code. Have a look at the glows of the circles and identify which glow is of which circle, whether it is outside glow or inset glow.

Now, we need eight coils on this tunnel. The coils are simple rectangles, but the major challenge is that we need the coils to run along the round path of the tunnel; not in straight line.

One way to do this would be to create eight small rectangles, shift their center to the center of the reactor, and rotate each coil by an increasing angle (in multiples of 45deg).

Let’s not complicate it and make one rectangle coil at a time:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<div class=”tunnel circle abs-center”></div>
<div class=”core-wrapper circle abs-center”></div>
<div class=”core-outer circle abs-center”></div>
<div class=”core-inner circle abs-center”></div>
<div class=”coil-1″></div>
</div>
</div>
.coil-1 {
position: absolute;
width: 30px;
height: 26px;
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

Now, we want to place this coil in the center at top of the tunnel. Like this:

Our reactor-container is 300px x 300px, so the center is at 150px from top and left. The tunnel is 220px wide, so its radius will be 110px. This gives us the top offset of the coil: 150px – 110px.

We can keep left of the coil to 150px, but since our coil is 30px wide, it will shift the middle of the coil by 15px to right, that’s why we need to subtract 15px from 150px to get the left offset of the coil.

We can either calculate these ourselves and put the value, or we can use the CSS calc() function. Let’s use the CSS calc() function to calculate the top and left properties of the coil:

.coil-1 {
position: absolute;
width: 30px;
height: 20px;
top: calc(50% – 110px);
left: calc(50% – 15px);
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

As you can see, the calc() function takes a mathematical expression as its argument and solves it.

Now we need eight such coils but they must lie on the tunnel. To do that, as discussed, we can simply place the eight coils at this same place, then transform their origin to the center of the reactor, and rotate each coil by an increment of 45 degrees.

We need to update the coil’s origin because by default it is set to the center of the coil; we want it at center of the reactor:

We will use transform-origin property to set the origin of the coil:

.coil-1 {
position: absolute;
width: 30px;
height: 20px;
top: calc(50% – 110px);
left: calc(50% – 15px);
transform-origin: 15px 110px;
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

The first value, 15px, in transform-origin is the x-offset (horizontal distance) from the top-left corner of the element, and the second value, 110px, is the y-offset (vertical distance) from the top-left corner of the element.

The coil’s origin is now at the center of the reactor, let’s rotate it by 45 degrees using the CSS3 transform property and see what happens:

.coil-1 {
position: absolute;
width: 30px;
height: 20px;
top: calc(50% – 110px);
left: calc(50% – 15px);
transform-origin: 15px 110px;
transform: rotate(45deg);
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

Great! That’s exactly what we want.

Before creating all the eight coils, let’s create a coil container div that will contain all the eight coils:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<div class=”tunnel circle abs-center”></div>
<div class=”core-wrapper circle abs-center”></div>
<div class=”core-outer circle abs-center”></div>
<div class=”core-inner circle abs-center”></div>
<!– the coil container –>
<div class=”coil-container”>
<div class=”coil coil-1″></div>
</div>
</div>
</div>

You will notice we also added a class “coil” to the “coil-1” element. We will keep all the common styles for coils in the “coil” class, and the individual coil element classes will only have their angle of rotation:

.coil-container {
position: relative;
width: 100%;
height: 100%;
}

.coil {
position: absolute;
width: 30px;
height: 20px;
top: calc(50% – 110px);
left: calc(50% – 15px);
transform-origin: 15px 110px;
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

.coil-1 {
transform: rotate(45deg);
}

The output will remain same.

Now, let’s make all the eight coils inside .coil-container:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<div class=”tunnel circle abs-center”></div>
<div class=”core-wrapper circle abs-center”></div>
<div class=”core-outer circle abs-center”></div>
<div class=”core-inner circle abs-center”></div>
<!– the coil container –>
<div class=”coil-container”>
<div class=”coil coil-1″></div>
<div class=”coil coil-2″></div>
<div class=”coil coil-3″></div>
<div class=”coil coil-4″></div>
<div class=”coil coil-5″></div>
<div class=”coil coil-6″></div>
<div class=”coil coil-7″></div>
<div class=”coil coil-8″></div>
</div>
</div>
</div>

…and give different rotations to all the coils (in increment of 45 degrees):

.coil {
position: absolute;
width: 30px;
height: 20px;
top: calc(50% – 110px);
left: calc(50% – 15px);
transform-origin: 15px 110px;
background-color: #073c4b;
box-shadow: 0px 0px 5px #52fefe inset;
}

.coil-1 {
transform: rotate(0deg);
}

.coil-2 {
transform: rotate(45deg);
}

.coil-3 {
transform: rotate(90deg);
}

.coil-4 {
transform: rotate(135deg);
}

.coil-5 {
transform: rotate(180deg);
}

.coil-6 {
transform: rotate(225deg);
}

.coil-7 {
transform: rotate(270deg);
}

.coil-8 {
transform: rotate(315deg);
}

Our reactor is almost ready.

Animating the Coils With CSS3 Animations

In Iron Man’s Arc reactor, the coils don’t move but they will in our reactor. We will animate the coils to rotate along the tunnel and will use CSS3 animations for this—no JavaScript.

To create an animation, you need to know the initial and final states of the object you are going to animate. We define these initial and final states in CSS by using @keyframes at-rule:

@keyframes reactor-anim {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

We want the element to be at 0 degrees and animate it until it reaches 360 degrees. And we named this animation as “reactor-anim.”

The element we want to animate is .coil-contailer. Notice, we didn’t define which object to animate yet, we have only defined the initial and the final state and name of the animation.

We need to link the element to the animation in order to take effect. We do it by using animation-name<code> property on <code>.coil-container:

.coil-container {
position: relative;
width: 100%;
height: 100%;
animation-name: reactor-anim;
animation-duration: 3s;
}

Notice, we also gave the duration of animation using animation-duration property. This defines how much time it should take to go from the “from” state to the “to” state defined using the @keyframes at-rule.

See the Pen Arc-Reactor-Ease-In by Kunal Sarkar (@supersarkar) on CodePen.

We need to change two things here: we want the animation to go on infinitely and we want a linear animation. You can see the animation is slow at the beginning, then fast, then again slow at the end—this behavior is defined by the timing function of an animation.

Let’s make these changes:

.coil-container {
position: relative;
width: 100%;
height: 100%;
animation-name: reactor-anim;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
}

We used animation-iteration-count property to set the animation to infinite, and animation-timing-function to make the animation linear, the default value of animation-timing-function is ease.

See the Pen Arc-Reactor-Linear-Infinite by Kunal Sarkar (@supersarkar) on CodePen.

We can combine all of these animation properties…

animation-name: reactor-anim;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: linear;

…into one shorthand property like this:

animation: 3s infinite linear reactor-anim;

Final Touches to the Reactor Container

Our reactor is ready, now let’s make some final changes to the .reactor-container. First, we will need one dark circle behind the reactor:

<div class=”fullpage-wrapper”>
<div class=”reactor-container”>
<!– dark circle behind the reactor –>
<div class=”reactor-container-inner circle abs-center”></div>
<div class=”tunnel circle abs-center”></div>
<div class=”core-wrapper circle abs-center”></div>
<div class=”core-outer circle abs-center”></div>
<div class=”core-inner circle abs-center”></div>
<div class=”coil-container”>
<div class=”coil coil-1″></div>
<div class=”coil coil-2″></div>
<div class=”coil coil-3″></div>
<div class=”coil coil-4″></div>
<div class=”coil coil-5″></div>
<div class=”coil coil-6″></div>
<div class=”coil coil-7″></div>
<div class=”coil coil-8″></div>
</div>
</div>
</div>

Let’s give a dark background and add some glow to it:

.reactor-container-inner {
height: 238px;
width: 238px;
background-color: rgb(22, 26, 27);;
box-shadow: 0px 0px 4px 1px #52fefe;
}

See the Pen Arc-Reactor-Semi-Final by Kunal Sarkar (@supersarkar) on CodePen.

See how the dark background and the glow creates an emboss effect?

Next, let’s make the .rotator-container round and give it some shadow and border, then we are done:

.reactor-container {
width: 300px;
height: 300px;
margin: auto;
border: 1px dashed #888;
position: relative;
border-radius: 50%;
background-color: #384c50;
border: 1px solid rgb(18, 20, 20);
box-shadow: 0px 0px 32px 8px rgb(18, 20, 20), 0px 0px 4px 1px rgb(18, 20, 20) inset;
}

See the Pen Iron Man’s Arc Reactor by Kunal Sarkar (@supersarkar) on CodePen.

Cheers! Our Arc Reactor is ready and even with a little animation as an added bonus. To level this up, we could explore using custom properties to create reusable variables for our color and number values for easier maintenance. Similarly, we could look into using a preprocessor—like Sass, Less or PostCSS—to write functions that do the mathematical lifting for us. Would love to see examples like that in the comments!