Note: To understand this tutorial, it definitely helps to have some preliminary knowledge of Sass/SCSS and Sass loops. We also use the checkbox hack in this tutorial, so knowing that can help too!

So I was browsing websites the other day, and stumbled upon a really cool word scramble effect. The website in question was definitely using JavaScript to create the effect, but I thought it may be possible to re-create the effect using only CSS animations and pseudo-elements. I created this attempt the other day:

All of the above was using psuedo-elements and the content property. It was a fun little "proof-of-concept" to do, but the code is disgusting - it's clumsy and you have to enter the scrambled letters for each content property at every keyframe step. Definitely not modular or time efficient - but I found a solution today using a feature of Sass called "maps". Maps operate a lot like JavaScript objects or JSON - which is perfect for our needs!

Quickly, here is a Sass map: $colors: (1: red, 2: blue, 3: green)

And here is how we would call something from it:

  .container
  background: map-get($colors, 1)
  // The background is now red. Not too bad, right?!

First, let's start with the HTML(Pug):

  .container
  input(type='radio' id='button1' name="button")
  label(for='button1') <span></span><span></span><span></span><span></span><span></span>
.container
  input(type='radio' id='button2' name="button")
  label(for='button2') <span></span><span></span><span></span><span></span><span></span>
.container
  input(type='radio' id='button3' name="button")
  label(for='button3') <span></span><span></span><span></span><span></span><span></span>

Essentially we are creating three containers, and inside each one we have an input, a label, and a crap ton of spans inside each label. The input is simply for the checkbox hack, so it is optional (read more about that here). The big key here is the spans inside of the label - we will be using the psuedo-element :after on those to create the actual word! So if your word is going to be "hello", we will need 5 EMPTY spans in there. The word will come with the css.

Now for the Sass set up. Like I said, we will be using maps to access all of our fun word scramble data! Here is the basic Sass variable / map set-up:

  
$colors: (1: red, 2: blue, 3: green, 4: yellow, 5: orange, 6: purple, 7: pink, 8: teal, 9: tomato, 10: red)

$scramble: (1: "a", 2: "#", 3: "x", 4: "?", 5: "r", 6: "%", 7: "w", 8: "b", 9: "q", 10: "!")

$count: 10

$finalWord: (1: 'H', 2: 'e', 3: 'l', 4: 'l', 5: 'o')

$finalWordCount: 5 


The $colors map has all of our randomized colors that we want - you can put whatever color in whatever quantity you want! The $scramble map has all the letters and symbols you want to randomly scramble-ize. And the $finalWord map is simply the final word that we want - in our case, "Hello". $count is simply the number of things in our maps; you can coordinate those numbers to be the same, like I did, or you can do two different numbers. You will see the time-saving advantage of maps shortly.

First we will start off by writing our word "Hello" by using the content property of the span:after elements. That's a mouthful, I know, but bear with me. We will access the map containg hello with a for loop! Check it out:

  @for $z from 1 through $finalWordCount
  span:nth-child(#{$z}):after
    color: black
    content: map-get($finalWord, $z)

Boom, this writes our word out. So, we make a loop. The loop counts the length of the letters in our word. We attach the loop variable to the nth:child():after, essentially selecting the psuedo-element of every span. In the content property, we put the map function in and put our $finalWord variable in, followed by our loop variable. This spells out our word! You can see where we are here:

Now we need to do our scramble animation. This requires us to make another loop that can access the $scramble and $colors maps. Here is what our code looks like:

  @for $x from 1 through $count
  input:checked ~ label > span:nth-child(#{$x}):after
    $increase: $x * 175
    animation: move-#{$x} ((1000 + $increase) + ms) linear forwards 1
    @keyframes move-#{$x}
      @for $e from 1 through 7
        #{$e}0%
          content: map-get($scramble, random($count))
          color: map-get($colors, random($count))


Here we essentially create another loop that counts up to the length of our $scramble and $colors maps. When our input is checked, we select the label and then each span child's after psuedo-element. Complicated selection, I know, but if you walk through it slowly you'll get it no problem.

We then create an $increase variable for our use later. We also call a CSS animation, and to make a unique animation for each span:nth-child():after element, we attach our loop variable to the CSS animation name. It looks like this: move-#{$x}. As for the speed of the animation, I take a base-line speed and then add our recently created $increase variable to it. This creates a nice staggered effect with the word!

We call the animation with the keyframes, and then create ANOTHER loop inside the animation. This makes it so we can generate a unique color and word for each step of the animation! Inside the animation itself we add the content and color properties, and both call a map. For content, the map parameter is our $scramble variable, and we randomize our $count variable to access any of our scrambled words. The $colors variable is the exact same; we will randomize the colors!

Now we have a word scramble! While this code does compile to quite a bit, it is SO much simpler to read and write than my first attempt. The maps feature of Sass definitely has a ton of potential, and I hope this was a pragmatic example of its ability. :D