Annual Kentucky Derby 8 ball

A list of tags for this post.

It’s the time of year when I make the annual Kentucky Derby Magic 8 Ball. It’s a fun little project that started in 2018 and unintentionally became a way for me to track the progress of my dev skills.

2018 was around the time I decided to improve my languishing dev skills. By chance I found a Magic 8 Ball JavaScript tutorial and decided to give it a go. Each year since I’ve been able to make improvements that were out of my range the year before. And each year I’ve been pleasantly surprised when I’ve found things easy to improve, or thought of an enhancement that wouldn’t have occurred to me before.

The wrap-up on the project page prior to this year’s update was “Hopefully by next year’s version I’ll be able to improve the accessibility further and maybe even give it some nice 8 Ball-ish animations”. Spoiler alert: I was able to!

Improved Accessibility

(permalink)

The accessibility was in pretty good shape as of last year. It can be used by keyboard only, there are no screen reflow issues and it announces reasonably well. One thing I did improve on this year is that the placeholder “8” was being announced.

The structure is that the “8” is replaced with random text, and I chose to keep it that way, but I added aria-hidden="true" to the default state. When the “Who will win?” button is clicked several things that happen, mostly adding and removing class selectors to hide and show the different elements.

This year’s ‘ah ha’ JavaScript moment was to realize that I could also manipulate attributes, so I added the following to the function to change aria-hidden from “true” to “false”:

document.getElementById('answer-container').setAttribute("aria-hidden", false);

Now the answer is announced but the “8” is not.

Animations

(permalink)

I rarely use any kind of animations. There are a number of reasons for this. Mostly my mind just doesn’t work in a way that would think “a little flourish or movement here would be just the thing”. Similarly when I look at animation code it doesn’t click for me the way grid or flexbox does.

One of the rare instances where it did occur to me that some animation would be just thing was the Magic 8 ball. And it seemed like straightforward shaking and fading would go a long way. Admittedly, I found some examples and made some adjustments rather than trying to figure out how to write it from scratch. That aside, I think does add to the overall Magic 8 ball-ness, which is nice.

Accessibility

(permalink)

While the animations are nice, what about accessibility? Motion can cause a variety of problems for people with motion sensitivities. Fortunately there are ways to help ensure that viewing animations and motion are a viewer’s choice.

As I was preparing to create this year’s 8 Ball Tatiana Mac’s article on prefers-reduced-motion was in my reading stream. Originally I was going to use the universal selector approach found in resets , but after reading Mac’s article I wanted to be a little more deliberate.

There are three items with motion.

  • The 8 ball shakes upon click of the button.
  • The button scales, giving the feel of pressing a physical button.
  • The answer fades in.

I particularly liked the suggestion of changing my mindset on the approach, so instead of the default being to allow animations, I set the default experience to no animations.

Instead of using prefers-reduced-motion: reduce to hide animations, I’m using prefers-reduced-motion: no-preference to allow the animations. The advantage of this approach, as Mac points out, is that browsers that don’t support the media query get the default no animation approach.

Default experience:

.shake {
animation: none;
}

button:active {
transform: none;
}

.reveal {
animation: none;
}

Animation experience for those who have not selected to reduce motion and are using a browser that supports the media query:

@media (prefers-reduced-motion: no-preference) {
.shake {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}

@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}

.reveal {
animation: fadeIn ease 4s;
} @keyframes fadeIn {
0% {
opacity:0;
}
100% {
opacity:1;
}
}

button:active {
transform: scale(0.95);
}
}

I’m usually so focused on how I can improve my dev skills that I overlook design. After adding the animations I realized it would also be nice to it make it look more like an actual 8 ball.

Side by side comparison of 2020 and 2021 Kentucky Derby Magic 8 Balls showing the 2021 version to look more like a tranditional 8 ball.
The 2021 version looks a bit more like the original Magic 8 Ball with a white circle and geometric 8.

After checking some reference material I made the circle containing the 8 white and used Exo font for the geometric 8 vibe. The blue triangle would be a nice touch, but it’s hard enough to get some of the longer character names to display well, and this is 8 ball-esque enough for me at the moment. Maybe next year I’ll try to improve the animation to make it look like the 8 ball is being turned over, but maybe not!

You can read all the about the improvements on a year-by-year basis at the project page.

A list of tags for this post.

Enjoying this site? Leave me a tip at Ko-fi!

Back to top