It’s hard to stifle a smile any time I get a glimpse of the future thanks to WebKit-based browsers like Safari and Chrome (and their mobile counterparts on iOS and Android devices). That happened today when I discovered a way to make an iPhone-style spinner without any images, just CSS.

First attempt

I have been working on a project that targets mobile WebKit browsers so it was natural to explore using -webkit-animation to handle the image rotation. My initial idea was to use a single PNG image as a mask over a colored background which makes it possible to change the color of the spinner in code. Here’s how that looked in CSS:

p#spinner {
  height: 62px;
  width: 62px;
  overflow: hidden;
  background: #000;
  -webkit-mask-image: url("data:image/png[...]");
  -webkit-mask-size: 62px 62px;
  -webkit-animation-name: rotate;
  -webkit-animation-duration: 1.5s;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
}

This is how it looks in a browser: Image mask rotation demo

The examples in this post all target WebKit browsers so I’d suggest you fire up Chrome or Safari for viewing the demos. Better yet, try a mobile WebKit browser.

It felt like a win to use a single-frame PNG image instead of an animated GIF but it didn’t look right. Webkit animations smoothly tween rotations so I ended up with what looked like a rotating image instead of the sharp ticking I was going for. Still, masking is an impressive technique that I’m sure will come in handy in another project.

CSS-only, no images

After several failed attempts to make the animation step-based, I still couldn’t get the effect right. I decided to look again into how I could do it without the image. A quick search led to a post by Kilian Valkhof who had a good idea for the basic technique. The first step is to create the bars of the animation by sizing and rotating them around an axis.

<style>
  div.spinner div {
    width: 12%;
    height: 26%;
    background: #000;
    position: absolute;
    left: 44.5%;
    top: 37%;
    opacity: 0;
    -webkit-border-radius: 50px;
    -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.2);
  }

  div.spinner div.bar1 {
    -webkit-transform:rotate(0deg) translate(0, -142%);
    -webkit-animation-delay: 0s;
  }

  div.spinner div.bar2 {
    -webkit-transform:rotate(30deg) translate(0, -142%);
    -webkit-animation-delay: -0.9167s;
  }

  [...]

  div.spinner div.bar12 {
    -webkit-transform:rotate(330deg) translate(0, -142%);
    -webkit-animation-delay: -0.0833s;
  }
</style>

<div class="spinner">
  <div class="bar1"></div>
  <div class="bar2"></div>
  [...]
  <div class="bar12"></div>
</div>

Each of the twelve bars is rotated 30° from the one previous and given an animation delay equal to 1/12 of a second. This ensures that the bars highlight, then fade in order. All that is left is to define and call the animation for each bar to fade.

@-webkit-keyframes fade {
  from {opacity: 1;}
  to {opacity: 0.25;}
}

div.spinner div {
  [...]
  -webkit-animation: fade 1s linear infinite;
}

Here’s the final animation: CSS spinner demo.

Make it scale

If you look closely at the code you’ll notice some odd percentage values. Using percentages to build the spinner means you can make it any size just by changing the width and height of div.spinner. It’s fully scalable. You can also append additional styles to change colors and add effects. Here are a few examples: Styled spinners demo.

And for those without a webkit browser handy, here’s what it looks like in Mobile Safari: