Skip to content
💾
Technology

How-To: 8-Bit Hovers

For The Yetee’s redesign, we wanted to come up with hover styles that were unique and kept with the brand’s playful feel. We played around with a few different options, but ultimately settled on these two 8-bit inspired hover effects.

See the Pen 8-bit Hovers by Tiffany Stoik (@tstoik) on CodePen.

Let’s walk through how they’re put together.

8-Bit Buttons

The idea behind these buttons is a pixelated side-swipe effect. When you hover, 5 bars stacked vertically slide in staggered fashion from the left. Each bar also has a square at the end of it, that flash between darker/lighter tones.

Note:

In the code below, my colors have been saved as variables, and I’m only showing the styles for the desktop media query for brevity’s sake. All media query styles can be found on CodePen. First, let’s set up our elements. These styles could work on regular anchors or buttons, but let’s use anchors for now.

<a href="#" class="btn red" title="Red">Red</a> <a href="#" class="btn green" title="Green">Green</a> <a href="#" class="btn blue" title="Blue">Blue</a>

Next, let’s get some basic styles for this button. Be sure to add z-index: 1 so that we can set a negative z-index on our hover panel. This will make sure it sits behind the text instead of in front of it, and that it’s not hidden behind the button’s background color.

.btn { position: relative; display: inline-block; margin: 0 10px; padding: 18px 30px; z-index: 1; border: 0; // in case this is a button element outline: 0; // in case this is a button element cursor: pointer; // in case this is a button element @include font-size(12); color: $white; text-align: center; line-height: normal; letter-spacing: 0.05em; text-transform: uppercase; font-weight: bold; // pseudo-border, so that the hover panel will cover it up on hover &:after { position: absolute; right: 0; bottom: 0; left: 0; height: 6px; z-index: -2; content: ''; } &.red { background: $red; } &.green { background: $green; } &.blue { background: $blue; } }

Now since we need five bars + five squares for the hover, we need more elements than pseudo-elements could provide. My strategy was to use a wrapper div for the whole hover, and a span for each bar, using pseudo-elements for the squares.

I could have accomplished the desired effect using fewer elements, but using one wrapper and one element for each bar makes the CSS much more straight-forward. We could add these extra elements directly in the HTML, but if you have a lot of buttons on one page, or even just a lot of other code on the page, it can get messy quickly.

I opted to add the element to the DOM with javascript (jQuery to be precise) on load. This is optional and you could definitely keep the extra elements in the HTML.

// add extra elements to buttons for hover, keeps html cleaner $('.btn').prepend('<div class="hover"><span></span><span></span><span></span><span></span><span></span></div>');

Now here’s where the complicated stuff starts to happen. Let’s style the hover panel and its children. We just want the hover panel to be positioned absolutely in the whole space of the button. Spans are going to be the vertical bars, with each span’s :after being used as the square.

Each vertical bar starts off with a different offset from the edge of the button, and has a different transition duration. To capture more of that 8-bit flavor, we’re using steps in the transition to make it less smooth.

.btn .hover { position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: -1; overflow: hidden; span { position: relative; display: block; left: -15px; height: 10px; width: 0; content: ''; } span:after { position: absolute; display: block; right: -10px; width: 10px; height: 10px; background: $white; // light-toned squares content: ''; } span:nth-child(odd) { &:after { background: rgba($black, 0.35); } // dark-toned squares } span:first-child { left: -75px; // staggered offset transition: all 0.3s steps(8); } span:nth-child(2) { left: -45px; // staggered offset transition: all 0.325s steps(8); } span:nth-child(3) { left: -55px; // staggered offset transition: all 0.35s steps(8); } span:nth-child(4) { transition: all 0.4s steps(8); } span:nth-child(5) { left: -25px; // staggered offset transition: all 0.375s steps(8); } }

Alright. Now for the actual magic. What happens when you hover? Actually not really that much. First, the spans transition to full width + offset + 1px — on some retina devices I found that without that extra pixel you could see the edge of the square. The transitions also have to be adjusted to mirror the unhover/blur transitions.

So the shortest transition duration above now gets the longest transition duration, etc. You should technically be able to just adjust the transition-duration property but when I tried this, there were some browser compatibility issues, so I’m just repeating the whole transition declaration.

The only other thing going on is the little squares flashing color. We’ve got one animation, running in alternate directions for alternate squares (without prefixes for sanity). They run for the same length of time but start at different offsets.

.btn:hover .hover { span:first-child { width: calc(100% + 76px); transition: all 0.4s steps(8); &:after { animation: whiteBlack 0.3s 0s 1 forwards; } } span:nth-child(2) { width: calc(100% + 46px); transition: all 0.375s steps(8); &:after { animation: whiteBlack 0.3s 0.06s 1 reverse backwards; } } span:nth-child(3) { width: calc(100% + 56px); transition: all 0.35s steps(8); &:after { animation: whiteBlack 0.3s 0.05s 1 forwards; } } span:nth-child(4) { width: calc(100% + 16px); transition: all 0.3s steps(8); &:after { animation: whiteBlack 0.3s 0s 1 reverse backwards; } } span:nth-child(5) { width: calc(100% + 26px); transition: all 0.325s steps(8); &:after { animation: whiteBlack 0.3s 0.07s 1 forwards; } } } @keyframes whiteBlack { 0%, 24% { background: $white; } 25%, 49% { background: rgba($black, 0.35); } 50%, 74% { background: $white; } 75%, 100% { background: rgba($black, 0.35); } }

And that’s it for the buttons!

8-bit Social Icons

Let’s start off with some anchors. I’m using a icon font for the icons, so we just need to use the appropriate classes.

<div class="social-buttons"> <a href="#" class="social-btn entypo-tumblr" title="Tumblr" target="_blank"></a> <a href="#" class="social-btn entypo-twitter" title="Twitter" target="_blank"></a> <a href="#" class="social-btn entypo-facebook" title="Facebook" target="_blank"></a> <a href="#" class="social-btn entypo-instagrem" title="Instagram" target="_blank"></a> </div>

Basic styles. We just want a big ol’ circle and some pretty colors for the icons.

.social-btn { position: relative; z-index: 1; display: inline-block; margin: 30px 15px; width: 96px; height: 96px; background: $white; border-radius: 50%; line-height: 96px; @include font-size(45); &:before { line-height: inherit; } // make sure our icons are in the right place &.entypo-tumblr { color: $navy-darker; } &.entypo-twitter { color: $blue-light; } &.entypo-facebook { color: $blue-darker; } &.entypo-instagrem { color: $pink; } }

Like with the buttons, I need more elements than I have. To get the 8-bit effect, we’re essentially going to “trace” the circle with squares and rectangles to create the pixelated look we want.

For each “side”, we want three elements: one rectangle to trace the main edge and two squares to “curve” down. To keep everything neat, I’m going to use jQuery to generate 4 spans, one for each side.

$('.social-btn').prepend('<span></span><span></span><span></span><span></span>');

Now let’s style them! They’re going to start out with no opacity and scaled down to zero. When we hover, they will scale back to normal. We’re just making this pixelated outline big enough that all the pieces fit together and outline the circle nicely. I went for a diameter of 108, 12 pixels bigger than the 96px circle.

The number looked good and was divisible by 2. I knew I wanted 10px corner pieces, so from there I just had to do the math to figure out what size the long pieces should be. Each rectangle is going to be 48px on its long side and 10px on its short side.

The squares are actually 10px by 16px or vice versa — wider for the top/bottom sides, taller for the left/right sides. They’re still visually 10px, we just need to make up that extra 12px to cover up any gaps where the sides meet the circle. In testing, they were a little jittery and had some painting issues.

Tossing both backface-visibility: hidden; and a perspective on them helped. The other thing that helped was transitioning only the transform on hover, and transitioning both transform and opacity on blur.

.social-btn { ... span { position: absolute; display: block; background: $white; opacity: 0; transform: scale(0); transition: transform 0.3s, opacity 0s 0.3s; backface-visibility: hidden; perspective: 1000; &:before, &:after { position: absolute; display: block; width: 10px; height: 10px; background: $white; content: ''; } } span:first-child { top: -6px; } // half the diameter offset span:nth-child(2) { right: -6px; } span:nth-child(3) { bottom: -6px; } span:last-child { left: -6px; } // top and bottom pieces span:first-child, span:nth-child(3) { left: 24px; width: 48px; height: 10px; &:before, &:after { width: 16px; } // original width + half diameter offset } // left and right pieces span:nth-child(2), span:last-child { top: 24px; width: 10px; height: 48px; &:before, &:after { height: 16px; } // original height + half diameter offset } }

Now let’s position the squares. Each square is a pseudo element on the rectangular span. We’re going to be moving by 10px in whichever direction is appropriate (eg: for the top piece, 10px down and 10px to the left/right).

 .social-btn { ... span:nth-child(2):before, span:last-child:before, span:nth-child(3):before, span:nth-child(3):after { top: -10px; } span:first-child:after, span:nth-child(3):after, span:last-child:before, span:last-child:after { right: -10px; } span:nth-child(2):after, span:last-child:after, span:first-child:before, span:first-child:after { bottom: -10px; } span:first-child:before, span:nth-child(3):before, span:nth-child(2):before, span:nth-child(2):after { left: -10px; } }

After all that, the hover is pretty simple. Grow it!

.social-btn:hover { span { opacity: 1; transform: scale(1); transition: transform 0.3s; } }

And that’s how you make 8-bit hovers! Be sure to check out The Yetee, where you can see these hovers in action, and lots of other awesome stuff.

Tiffany Stoik, Front-End Developer