Using CSS3 Transitions to Animate the (Yahoo Weather App’s) Rising Sun

The Yahoo! Weather app for iOS is a triumph of form meeting function. One of its fun visual tricks is how the sunrise and sunset times are presented:

Although the Yahoo! app is a native iOS app, MobileSafari’s CSS3 support seems powerful enough that I thought it would be fun to try re-implementing this small feature using only web technology.

The skeleton app

First, let’s get the HTML structure in place. I’m going to build the arc using a border-radius and use some absolute positioning to get the sunrise and sunset times in place. Because we’re going to be animating things, I’m going to use a nested div structure as follows:

Div Structure

With that model in mind, we can build the following HTML structure:

<div class="sunmoon">
    <h2>Sun &amp; Moon</h2>
    <div class="sun-times">
        <div class="sun-path">
            <div class="sun-animation"></div>
        <div class="sun-symbol-path"><span class="symbol">☀</span></div>
    <div class="legend">
        <div class="sunrise">5:30 AM</div>
        <div class="sunset">8:04 PM</div>
    <div class="clear">&nbsp;</div>

By using a border-radius on the .sun-path we can use the .sun-animation div as the yellow filling. When we animate the expansion of that div it should show, like the native app, the evolution of the sun through the day.

Here’s the initial CSS:

$arc-diameter: 170px;

.sun-times {
    margin-top: 40px;
    width: 230px;
    height: 60px;
    border-bottom: 1px solid #999;
    overflow-y: hidden;

    .sun-path {
        margin-left: 25px;
        width: $arc-diameter;
        height: $arc-diameter;
        overflow: hidden;
        border: 1px dashed #999;
        border-radius: 50%;

This creates a nice arc for our sun and glow to travel along, thus:

Basic arc

Letting the light shine through

The original Yahoo app animates the filling of the area below the arc, using what looks like a custom easing function. The built-in ease-out function that CSS transition supports should be a a pretty close approximation so we’ll use that.

To get the perfect smoothness, we want the animation to be hardware accelerated. On iOS, this means using a CSS3 transform. However, MobileSafari fails to observe the overflow: hidden property over the border radius. Instead of filling in the area under our arc, it glitches and fills in a rectangle until the animation is complete, (at which point the border-radius is observed again).

We can work around this by extending the width of the div, instead of transforming it. Unfortunately that means our animation won’t be hardware accelerated. This is a tough pill to swallow: it will dramatically reduce the smoothness of the whole component. Thankfully we can still animate the area below our arc using only a few lines of CSS, as follows:

.sun-animation {
    width: 0px;
    height: 150px;
    background-color: rgba(255, 255, 0, 0.4);
    -webkit-transition: width 2s ease-out;
    transition: width 2s ease-out;

And here we can trigger the animation in Javascript:

$('.sun-animation').css('width', '70%');

This will grow the yellow translucent area over a two second period and ease the speed of the animation as the glow approaches its final destination.

Adding the sun

Adding the sun that travels along the arc involves a few things: first, finding a way to pictorially render a sun , then deciding how to make the sun travel, and finally synchronizing the path of the sun so it moves with the expanding area below the arc.

Unicode is a wonderful thing: rather than create an image in photoshop we can use one of two sunshine characters and apply color and text-shadow to have the sun really shine. If you have the correct font support, these characters should just work: ☼ ☀. If not, visit the unicode reference pages for “black (or white) sun with rays”.

To get the sun animating, let’s have it rotate around the same arc that we’re filling, using a CSS rotation transform. We’ll make the .sun-symbol-path a tall div with the sun at its top, and then rotate it so it moves around the arc. I use the webkit vendor prefixes here, as iOS still requires them:

.sun-symbol-path {
    position: absolute;
    left: ($arc-diameter / 2) + 25px;
    bottom: -5px;
    height: $arc-diameter / 2;

    color: yellow;
    text-shadow: 0 0 5px black;

    // Root the rotation at the bottom of an invisible column
    -webkit-transition: -webkit-transform 2s ease-out;
    -webkit-transform-origin: 50% 100%;

    // Set our initial rotation to point not upwards, but towards
    // the bottom-left of the widget.
    -webkit-transform: rotateZ(-73deg);

    .symbol {
        position: relative;
        font-size: 16px;
        top: -8px;

To trigger the sun’s animation, we use Javascript to set a new rotation. The browser will use the -webkit-transition to take care of animating it for us with full hardware acceleration. All it takes is the following line of Javascript:

$('.sun-symbol-path').css('-webkit-transform', 'rotateZ(25deg)');

Now it’s starting to look like the real, native weather widget. However, if you look closely at the running codepen you’ll notice that the movement of the sun isn’t 100% in sync with the filling of the arc!

80% done, but 80% still to do…

At this point we need to stop and take a step back. Why aren’t they in sync? It’s because the expanding width only travels on a horizontal distance but the arc of the sun travels along the entire arc: the sun have to go a longer distance. Because both animations are specified (in the transition propery) to take the same amount of time, their horizontal position is clearly not going to stay in sync when they’re traveling different distances.

This is complicated further by the presence of an easing function: we could figure out the mathematics of a linear animation, and even let the browser know using CSS (to guarantee the hardware acceleration). But as soon as we want to use a non-linear easing function like ease-out, the mathematics becomes a bucket-load more complex and we’d have to start generating an arbitrarily large number of classes just to make everything work smoothly. That would not be a cool hack, or a good use of CSS.

A more “correct” approach would calculate the exact horizontal position that the sun should be at (on the arc) for each and every pixel of width that the filled-in arc grows by. Doing this would mean we’d have to specify the animation purely in Javascript, not in CSS, and the hardware acceleration would be much harder to achieve.

What does that mean?

I’ve learnt two take-home lessons from this exercise:

  1. MobileSafari’s CSS3 support is pretty awesome, but has the occasional browser bug to watch out for when you near the bleeding edge
  2. CSS alone is not enough for all animations that look (but aren’t) very simple.

Interesting exercises for the enthusiastic reader include:

  1. Experimenting with a key-framed approach using -webkit-animation to improve the synchronization of the sun and the glow.
  2. Writing a Javascript function that takes the sunrise and sunset times and correctly animates to a position representing the current time of day. (The examples above use a fixed width/position to simplify the demo).

I have a series of other articles planned about how we can use CSS to incorporate some of the best UI techniques of the Yahoo! iOS weather app: it contains a UX gold mine of attention to detail. In the meantime, Chris Coyier of CSS Tricks has also published a great tutorial on how to recreate the interactive side-swiping parallax scrolling feature of the app.

Next steps

to find out when my follow-up posts are published, and for more front-end tips & tricks every two weeks.

Have a play with the full CSS and HTML code sample on codepen:

CSS3 Animating Sun

(View it on your iPhone for the best effect:

Have you come across many browser bugs in the mobile world? Got any epic CSS3 hacks to share? Let me know in the comments below, thanks for reading!

This entry was posted in CSS. Bookmark the permalink.

2 Responses to Using CSS3 Transitions to Animate the (Yahoo Weather App’s) Rising Sun

  1. porter says:

    You could use images with the id’s for the animations instead of nesting multiple div’s with background images, but nice post on basic animations.

    • lee says:

      One nice consequence of using divs is that the animation doesn’t need to block waiting for image resources before it can start.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s