In the run up to January’s graduation ceremonies we were tasked to come up with something fancy to celebrate. The winning idea was to replace the main homepage banner with the names of students graduating on that day.
After initially thinking we’d show a list of names, we realised that for each Graduation day we would have upwards of a thousand names to display, so showing a static list was not a practical solution. Instead, we decided to switch names out one at a time in random order until each name had been shown; kind of like stars twinkling in the night sky. The idea stuck and from then on we started referring to our new component as the Graduation Twinkler.
To achieve this in the browser the basic plan was to split the names into separate layers, each positioned one on top of the other in such a way that the “slot” for each visible name lined up with the one below. Once we had that we could then add some code to randomly cycle through the name slots, hiding the current name and revealing the next one.
Addressing the layout
The first issue we came across was the layout from our initial concept not working well. When we got our hands on the real list of student names that were to appear on the first day of the ceremonies we found a huge amount of variation in their lengths. The shortest names were a mere 5 characters – the longest name on the other hand was a whopping 41 characters long.
This presented a problem for our column layout. To accommodate the longest names the columns would need to be made very wide, which in turn doesn’t work very well for shorter names.
Fortunately, CSS has just the right tool for this: Flexbox. Using the CSS flexbox module allows us to lay out the names much like words in a paragraph; we don’t have to worry about sizing any columns and names will automatically wrap around at the end of a line making the layout responsive by default.
Because we wanted to make sure that the name slots in each layer lined up, we first sorted them by length before splitting them into distinct layers. After that we shuffled the order of the slots to give our list an organic feel.
We then had a decent starting point for our Twinkler. Here it is with just the first layer visible:
You may notice that the Twinkler has already been split into two parts: above and below the main heading. We run two instances of the Twinkler in parallel by splitting the list into halves from the onset because it’s easier to handle than having to account for the heading in the middle of a single component.
Next up – getting ready to start adding the names.
Aligning the layers
So far so good. But as it stands the names slots don’t quite match up between the layers, so although each layer will look fine on its own, when we start cycling through the names we end up with some overlapping or, even worse, causing line breaks in different places and throwing everything out of sync.
To fix this, we made all the layers visible at the same time and added a border around each name to see how well (or not) they line up:
The solution to this was to work out a “safe” width for each slot and apply it to each slot while we’re building the layers. The formula ended up looking like this:
width = parseFloat( ( max_length * 0.7 ) + 0.1 ) + "em";
When this code runs, the result will be something like
7.8em. A quick breakdown of some of the parts:
max_lengthis the number of characters in the longest name in the slot, calculated just before this snippet.
emadded at the end is a unit of measure used in CSS. Ems are a unit of measure based on the size of the text; when the size of the text changes the relative size of our name slots will remain the same. For example our website’s stylesheet will set a different font size depending on the size of the user’s screen – using this unit of measure ensures the formula above will work regardless of the user’s device or screen size.
The 0.7 and 0.1 values in the formula were found after a bit of trial and error. Multiplying the maximum number of characters in a name slot by 0.7em gives us a good approximation of the actual size that the slot will need on screen and adding 0.1em gives us a safety buffer for names that contain lots of wider characters.
Here’s what our layers look like now:
Perfect – everything is nicely lined up. Time hide the extra layers again and start changing the names.
Getting the names to change
Without going into too much detail, here’s how it works:
- Make a big list of all the name slots
- For each slot, create a list of references to each name and a cursor to indicate which is currently visible – this will be our name cursor for each slot
- Sort the list of slots randomly – this is the order in which the slots will cycle
- Create a counter to track which slot is going to change next – this will be our slot cursor
- Hide the name at the name cursor position for the slot at the cursor position
- Increment the name cursor position for the current slot – go back to the first name if we’re at the end
- Display the name at the new name cursor position
- Increment the slot cursor to move on to the next slot – go back to the first slot if we’re at the end
- Wait a random amount of time between 0.5 and 1.5 seconds
- Go back to step 5 and repeat indefinitely.
Here’s the result:
Adding the twinkle ✨
So we now have the basic mechanics in place – great. It looks somewhat boring though; time to add some animation and make it look a bit snazzier.
- Hidden before being shown
- Hidden after being shown.
We need the two distinct “hidden” states to allow us to create different animations for when the name is being revealed, as opposed to when it is being hidden. If we only had visible and hidden states then we would be limited to using the exact same animation to hide the name as we used to show the name, just in reverse.
With the different states set up, all we had to do was write a bit of CSS to set the appearance of the name in each state, then with the addition of the CSS transition property, the browser would understand that we want to animate the transition between our states.
The options here are pretty much limitless but it became quite clear early on in experimenting that simple animations were going to work better. With up to 100 names visible at any given time the animation would quickly become overwhelming if we didn’t keep it fairly low-key.
Here’s an example of a drop in/out effect:
… or another with a rotation on the X axis and exaggerated perspective:
In the end though, we settled on a zoom/fade effect that gave us a good balance between visual interest and cognitive load:
A pause/resume button
To ensure that our Twinkler isn’t a hindrance to users who may be distracted by the animation, we need to provide a means of pausing or stopping it. In fact, it is a requirement to do so to meet accessibility requirements. See Understanding Success Criterion 2.2.2: Pause, Stop, Hide from the Web Content Accessibility Guidelines for the full details.
We created pause/resume functionality by adding a button to the Twinkler, then attaching an event listener to it. Event listeners allow us to capture, and react to, a wide range of things that can happen on a webpage or in a browser. In short, we will be telling the browser “if there’s a click on this button, run this piece of code here”.
The code that we run is straightforward in its logic: if the Twinkler is already running then cancel the timer that waits between each name cycling. However, if the Twinkler isn’t running then start it up again.
The final result
Check out the full-sized version on Codepen.
I hope you’ve enjoyed this quick dip into the inner working of the Graduation Twinkler. If you’re after more detail, the full code is available to browse on Codepen.
P.S. Although the names used in the demos above are fake, I’ve added a few extras to the list – can you spot them?