Building a button

The link at the top of this page is a plain-jane, run-of-the-mill, vanilla, nothing-special link. But it can be so much more! Let's turn it into something to write home about!

Setting ourselves up

To get started, let's use a bit of positioning to keep that link visible on the page where we can see it as we continue working through this exercise.

  1. Create and link a stylesheet for this exercise.
  2. Make a ruleset that selects the header element, with the following rules:
  3. position: fixed;
  4. top: 0;
  5. left: 0;
  6. width: 100%;
  7. height: 200px;
  8. background: #000;

This made a fixed page header, but it's covering our page content. We can fix that.

  1. Add a selector for the body element, and set padding-top to be the same as our header height.

Now let's center the link in our page header. Instead of using padding or margin to push the link around, let's use absolute positioning. This link is not going to be styled like a standard link, so probably best if we give it a class, rather than just selecting by tag name.

  1. In the html, add class="launch-button" to the a element.
  2. In your stylesheet, add a selector for .launch-button. Add the following styles:
  3. position: absolute;
  4. left: 50%;
  5. top: 50%;

The link is now in the middle of the header... sorta. It's actually riding a bit low and right, because we haven't actually positioned the link's center, we've positioned it's left and top edges. For the link to be truly centered, we need to offset back toward the top by half the link's height, and toward the left by half the link's width. We could try to measure the link and do the math, but it makes more sense to have the browser do the math for us. We'll do so by using a new css property: transform. The transform property is used to visually transform an element by skewing, rotating, scaling, and/or translating (moving).

  1. Add the following to your .launch-button selector:
  2. transform: translate(-50%, -50%); (The two numbers correspond to horizontal and vertical.)

The link is now exactly centered... even though we don't know the exact width of either the header or the link itself. It's a useful technique for centering an absolutely-positioned item in its container.

This approach may seem a bit counter-intuitive, because it looks like we asked the link to shift left by 50%, then back right by 50%. So why didn't it go back to it's starting position? The answer is that when we use a percentage as a unit for positioning we're referring to a percentage of the parent container. For transform, on the other hand, percentages refer to the element being transformed. So what we're really doing here is saying "Move from the left side half the width of the header, (left: 50%;) then move back by half the width of the link ( transform: translate(-50%, -50); ). Similarly, move down from the top by 50% the height of the header, then back up by half the height of the link.

The link has been located in our 'hero' position... time to style it.

Making it 'button-y'

Let's get rid of some default 'link' styles, and add some 'button' styles.

  1. In the .launch-button ruleset, add:
  2. text-decoration: none;
  3. background-color: #aa0000;
  4. color: white;
  5. font-size: 20px;
  6. padding: 15px 30px;
  7. border-radius: 10px;

No surprises here, we've used all these properties before. Let's reinforce the interactive nature of the button by giving it a :hover state and :active state.

  1. Create a new ruleset for .launch-button:hover. Add:
  2. background-color: #cc0000;
  3. box-shadow: 0 0 8px #ff0000;
  4. Create a new ruleset for .launch-button:active. Add:
  5. background-color: #750000;
  6. box-shadow: 0 0 2px #750000;

The box-shadow property is one you may not have used. It's very flexible, and can make all kinds of shadows/glows. You can read the docs to learn more, or you can use a box-shadow generator.

Our button now has a fun glow when we hover, and darkens when pressed. It feels like a button... but maybe not a very exciting one.

Adding an icon

We've used font-awesome before to add icons. We'll use it here as well... and we're going to use the transform property to have some fun with them. This page already has the font-awesome library added in the head, so we're good to go.

  1. Inside the a class="launch-button" element here in the html page, on a line right after the word "launch" but before the closing /a tag, add the following html:
    <div class="rocket">
      <i class="fas fa-burn"></i>
      <i class="fas fa-rocket"></i>

The i tag normally italicizes text, but font-awesome has appropriated it for adding icons. We've added two icons: "burn" and "rocket."

Now, let's put these icons where we want them to go. I'd like to have the rocket sitting to the right of the text, pointing straight up, with the flame coming out the bottom. The div class="rocket" container will serve as a positioning parent for the icons, and the container itself will also be positioned within the button.

  1. Create a selector for .launch-button .rocket and set:
  2. position: absolute; This makes it a positioning parent for its descendant elements, in this case, the icons.
  3. Create a selector for .launch-button .rocket i, which selects both icons. Set:
  4. position: absolute;
  5. top: 0;
  6. left: 0;

The icons are now smashed together in the .rocket container, absolutely-positioned in the same place. Now let's use transform to arrange them the way we want.

  1. Select .launch-button .fa-rocket and set transform: translate(-50%, -50%) rotate(-45deg);. This centers the rocket in its container (just like we did the button in the header), and rotates it to point straight up.
  2. Select .launch-button .fa-burn and set transform: translate(-50%, -15%) rotate(180deg) scale(0.3);. This centers it horizontally, aligning it with the rocket, but leaves it hanging lower vertically, so it's toward the rocket exhaust nozzle. It also rotates it 180°, and makes it smaller.

The overall rocket is still in the wrong position. Let's make room for it to live next to the button text using padding, then position it in the space we make.

  1. In the .launch-button ruleset, change padding to 15px 60px 15px 30px;. We're opening a space on the button's right side.
  2. In our .launch-button .rocket ruleset, add:
  3. top: 50%; Centers the rocket vertically.
  4. right: 40px; Positions the rocket 40px from the right edge, in the gap we made.

The flame gets a bit lost, let's use color!

  1. In the .launch-button .fa-burn ruleset add color: orange;.

Seems like the rocket should have some interaction on :hover and :active too, doesn't it? Let's progressively increase engine power as we approach launch!

  1. In .launch-button .fa-burn, change the value for scale to 0.1. The booster will be 'off' by default.
  2. Add a ruleset for .launch-button:hover .fa-burn. Note how this selector works. It's saying "Select elements with the .fa-burn class that are inside of a hovered .launch-button. So even though we're styling the flame, this ruleset will be applied when the button is hovered, not the flame icon itself.
  3. Set transform: translate(-50%, -15%) rotate(180deg) scale(0.4);. We're overriding the default transform to make the flame bigger on hover.
  4. Create another selector for .launch-button:active .fa-burn, and set transform: translate(-50%, 25%) rotate(180deg) scale(0.8);. We're overriding to make the flame still bigger on active. We're also shifting it downward so the whole flame is exposed.

Seems like the whole rocket could also get bigger on hover, to make it pop a bit. We'll scale the container that holds both icons, treating our rocket arrangement as a single element.

  1. Create a selector for .launch-button:hover .rocket to select the .rocket container when the button is hovered. Add transform: scale(1.3);.

Launching the rocket

Could we? Make the rocket actually launch? I think we can!

First, let's create an environment to launch into, using a linear-gradient background.

  1. In the selector for the header, replace background with background: linear-gradient(to bottom, #000000 40%,#0560aa 80%,#7cd4ef 100%);.

I used The Colorzilla Gradient Generator to make it easy.

Now, when the button is active, let's push the button back and bring the rocket up and forward.

  1. In .launch-button change top to 75%. We're moving the whole button downward to make room for it to launch above.
  2. In the .launch-button:active ruleset, add transform: translate(-50%, -20%) scale(0.7);. We're pushing the button down and scaling it way back when active.
  3. Now create a .launch-button:active .rocket ruleset to select the rocket, and add transform: rotate(-15deg) translate(60px, -140px) scale(3.5);. We're launching the rocket away from the button.

That's pretty fun! I feel like the label of the button is a little plain though, and it's strange that there's still a gap next to it where the rocket was. Let's clean it up. We'll start by adding a span around the word "Launch" so we can style it safely without affecting the icon style.

  1. Add a span in the html, so Launch becomes <span class="label">Launch</span>.
  2. Create a ruleset for that label, and add the following styles:
  3. letter-spacing: 0.1em;
  4. text-transform: uppercase;
  5. color: white;
  6. Now, create a new ruleset to select the label when the button is hovered. Add:
  7. color: orange;
  8. text-shadow: 0 0 3px rgb(251, 255, 0, 0.5);
  9. In the .launch-button:active ruleset, add padding: 15px 30px; to take away the gap when the rocket is safely away.

Note the text-shadow? It's similar to box-shadow, but for text. Here's a useful generator.


What's really going to send this thing over the moon (sorry, I'm a dad) is transitioning from one state to the next, instead of just 'jumping' from default to hover to active. CSS lets us do that with the transition property. Strap yourself in. (I'm on fire!)

  1. In the .launch-button ruleset, add transition: transform 0.5s, background-color 0.1s, padding 0.5s, box-shadow 0.1s;. Note how we are giving a list of properties to transition, each with a duration. So padding, for instance, will transition over 0.5 seconds.
  2. In the .launch-button .rocket ruleset, add transition: transform 0.5s;.
  3. In the .launch-button .label selector, add transition: color 0.2s, text-shadow 0.2s;.
  4. In the .launch-button .fa-burn selector, add transition: transform 0.4s;.

Press the button and marvel at what you have made!


You might think we're done... you might want to be done... but no, we have one final area to explore. Transitions are great, but what if you want an animation to keep going?

  1. Copy the following into your stylesheet:
    @keyframes burn {
      from { transform: rotate(-7deg); }
      33% { transform: rotate(4deg); }
      66% { transform: rotate(-4deg); }
      to { transform: rotate(7deg); }

We've just added a set of keyframes with the name burn. The keyframes define a series of changes to a property or set of properties. But if you look at the page, you won't see any change yet. That's because we have not applied these keyframes to any element.

I want to apply them to our flame icon, to give it a 'shaking' effect. However, the flame already has a transform on it, and I don't want to mess with that. The inspector reveals, however, that the actual icon is in a ::before element, because that's how font-awesome does it, which means I can style the ::before element without changing anything about the i tag that contains it.

So let's apply this animation.

  1. Create a .launch-button:active .fa-burn::before ruleset, and add:
  2. display: block; Not sure why, but needs this to animate.
  3. transform-origin: center bottom; This means "apply the transform around an axis at the base of the flame, not the center of it."
  4. animation-name: burn; This tells the element that it should use the "burn" keyframes.
  5. animation-duration: 0.1s; Specifies the time over which a single 'loop' of the keyframes should span.
  6. animation-iteration-count: infinite; This tells the animation to loop without end (as long as this selector applies).
  7. animation-direction: alternate; This tells the keyframes to alternate forward and backward in time, 'bouncing' back and forth.

As you appreciate what you've made, be sure to make appropriate rocket-booster noises with your mouth.

Our mission is complete. In the process of styling this button we've introduced linear gradients, box shadows, text-shadows, transforms, transitions, and animations. It's as far as I will take you for now, but know that each of these things has tremendous capability and flexibility, and you should treat this as only a single example of how and where they might be applied. While we used them for a button, there's nothing that says they can't be used elsewhere, and in infinite combinations. Explore!