Astronomy has been a constant interest of mine, so I try to integrate it into as many pieces of work as I can. In this case, I wanted to represent a basic animated day-night cycle for the background of a web page.

SVG For the Daylight Sky

SVG gradients are very similar to CSS gradients: indeed, their syntax heavily influenced theWebkit team’s first interpretation of the spec. The version retains one significant advantage: it can be animated.

Most examples of SVG gradients that you will find use hexadecimal colors. They can actually use any CSS color system, just like their CSS equivalent. In this case, alpha color values will be particular useful, as I want the sky gradient to be partially or wholly transparent at “night”, revealing a starscape.

I started with the following, representing “midnight”:

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
	<linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
		<stop stop-color="rgb(0,0,12)" offset="0%" id="zenith"></stop>
		<stop stop-color="rgb(0,0,12)" offset="100%" id="horizon"></stop>
		</linearGradient>
<rect id="sky" x="0" y="0" width="100%" height="100%" style="fill:url(#skyGradient)" />
</svg>

The linear gradient is displayed vertically, with the zenith at the top, and both points starting with the same color. The resulting gradient is applied to the sky rectangle, which occupies the entire space.

Both stop points are animated over 24 seconds, representing the hours in a day. Both components are color animated; the first stop-point’s position on the vertical gradient line is also transitioned, which changes the color range distribution in the gradient:

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
	<linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
		<stop stop-color="rgb(0,0,12)" offset="0%" id="zenith">
			<animate attributeName="stop-color" dur="24s"    values="rgba(0,0,12,0);rgba(2,1,17,0);rgba(2,1,17,0);rgba(2,1,17,0);rgba(32,32,44,0.5);rgba(64,64,92,.8);rgb(74,73,105);rgb(117,122,191);rgb(130,173,219);rgb(148,197,248);rgb(183,234,255);rgb(144,223,254);rgb(87,193,235);rgb(45,145,194);rgb(36,115,171);rgb(30,82,142);rgb(30,82,142);rgb(21,66,119);rgba(22,60,82,0.8);rgba(7,27,38,.5);rgba(1,10,16,.3);rgba(9,4,1,0);rgba(0,0,12,0);rgba(0,0,12,0)" repeatCount="indefinite" />
			<animate attributeName="offset" dur="24s" values="0;.85;.6;.1;0;0;0;0;0;.01;0;0;0;0;0;0;0;0;0;0;.3,.5,.8,0"repeatCount="indefinite" />
		</stop>
		<stop stop-color="rgb(0,0,12)" offset="100%" id="horizon">
			<animate attributeName="stop-color" dur="24s"    values="rgba(0,0,12,0);rgba(25,22,33,.3);rgba(32,32,44,.8);rgb(58,58,82);rgb(81,81,117);rgb(138,118,171);rgb(205,130,160);rgb(234,176,209);rgb(235,178,177);rgb(177,181,234);rgb(148,223,255);rgb(103,209,251);rgb(56,163,209);rgb(36,111,168);rgb(30,82,142);rgb(91,121,131);rgb(157,166,113);rgb(233,206,93);rgb(178,99,57);rgb(47,17,7);rgb(36,14,3);rgb(47,17,7);rgba(75,29,6,.4);rgba(21,8,0,0);rgba(0,0,12,0)" repeatCount="indefinite" />
		</stop>
</linearGradient>
<rect id="sky" x="0" y="0" width="100%" height="100%" style="fill:url(#skyGradient)" />
</svg>

While it uses a slightly different syntax from CSS animations, the <animate>element is fairly straightforward: it defines the property that is being changed, a duration, and a list of values to be animated between.

An Image For The Night

The <body> element has a starfield image covering it: this will be hidden during the “day” by the opaque colors of the sky.

body { 
	margin: 0;
	position: relative;
	background-color: #000;
	background-image: url(starfield_stock_1.jpg);
	background-size:cover;
}

The night sky will revealed when the opacity of the colors in the gradient are lowered, allowing the image to show through.

The Sun As a CSS Gradient

The “sun” is a simple <div> element, made circular with border-radius and provided with a glow using a combination of radial-gradient and box-shadow:

.sun {
	width: 15vw;
	height: 15vw;
	background: #ff0;
	background: radial-gradient(ellipse at center, #f90 0%,#fc0 60%,#ff0 100%);
	border-radius: 100%;
}

vw units are used for the width and height of the element to keep the sun both perfectly circular and responsive.

We want the sun to rise from below the edge on the “east” (the bottom right of the page) ascend through the sky, and descend towards the left. The body is already position: relative, so I can use position: absolute on the sun to locate it on the horizon:

.sun {
	position: absolute;
	bottom: -7vw;
	right: 7vw;
	transform-origin: -28vw 7vw;
}

At the same time, I want to rotate the sun around a fixed point in the center of the page; to do this, I move its transform-origin to the left, placing it at the center of the page.

As the SVG animation starts at midnight, the sun needs to start below the page; in other words, underneath the world we see.

@keyframes suntrack {
	from { transform: rotate(.15turn); }
	to { transform: rotate(-.85turn); }
}

This animation rotates the sun in a complete circle, per my earlier article. I also wanted the sun’s rays to “pulse” subtly as it moves:

@keyframes sunpulse { 
	from { box-shadow: 0 0 100px #ff0, 0 0 100px #ff0; }
	to { box-shadow: 0 0 50px #ff0, 0 0 75px #ff0; }
}

Both animations then need to be called:

.sun {
	position: absolute;
	bottom: -7vw; right: 7vw;
	transform-origin: -28vw 7vw;
	animation: suntrack 24s infinite forwards linear,
		sunpulse 2s alternate infinite;
}

The movement of the sun is linear (i.e. it doesn’t speed up or slow down as it moves across the sky), but the pulse has a “pingpong” effect, combined with default in-out easing.

Conclusion

I have not added a moon to the animation, although it would certainly be possible: in this case, the simplest method would be to add it opposite to the sun, like children at the opposite ends of a see-saw.

Obviously, this animation does not reflect the current time in your location, nor the correct declination of the sun for the season or weather conditions. It’s my intention to recreate all of those features in future articles.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/dtgGl