A Sunset Animation 🌇️

We’re going to create an animation of a breathtaking sunset. It will be deeply romantic.
What’s more, we’re going to make it with SVG, from scratch!
The basic tools that you will need are a text editor and a browser - you can find more info about text editors in the Tools for Working with SVG resource. If you already have a fancy editor (like Atom, VS Code or Brackets) then you’ll be able to install an ‘SVG Preview’ extension to automatically refresh your image while you edit it.
This is an in-depth tutorial assuming no experience with SVG, so you'll need 30mins if you're just reading along, and longer if you're completing the steps yourself (highly recommended!)

Contents

  1. The Bare Necessities of an SVG of an SVG
  2. ⚫ Drawing a circle
  3. Fill the screen with height
  4. The viewBox
  5. WTF? Understanding the viewBox
  6. Positioning and Coloring Elements
  7. ⏹️ rect
  8. ⭐ polygon
  9. Adding an id
  10. Layers upon Layers: z-depth in SVG
  11. <g> Groups </g>
  12. Finishing our Drawing
  13. Animation in <style>
  14. Creating a CSS ruleset
  15. Finally some goddamn animation.
  16. Looping
  17. Animation direction
  18. Easing
  19. Another Animation
  20. 💎 Adding some class
  21. SVG in HTML

SVG is a type of Extensible Markup Language (XML) - it uses tags to format or ‘mark up’ its content.
The bare minimum required for a standalone SVG file are some opening and closing <svg> tags.
The opening tag must containing the XML namespace declaration, which tells anyone who is interested that this is an SVG file, and exactly which definition of ‘SVG’ you mean by that.

<svg xmlns="http://www.w3.org/2000/svg">
        <!-- ✏️ your drawing here 🖌 -->
      </svg>
      

Note: the SVG element has an opening <svg> tag and a closing </svg> tag (← see the backslash?) This is so that we can put other elements within the SVG element.

Save this file, and give it a .svg extension, eg. ‘Cool-Drawing.svg’. Wow, cool drawing.
But this is just an empty SVG file so far. If you open it in the browser, you’re going to be disappointed. You still need to draw your image within these <svg> tags.

This is the simplest SVG drawing that I think it is possible to make. It has a tiny wee filesize of just 63 bytes.
Let’s draw a <circle> element with a radius of 4 units: r="4"

<svg xmlns="http://www.w3.org/2000/svg">
        <circle r="4"/>
      </svg>
      

We will not be putting any other elements within our circle element, so we can make it a single ‘self-closing’ tag, with a backslash at the end, like so: <circle/>

If you open this file in the browser, you’ll see that it’s not just the filesize that’s small:

My-Cool-Drawing.svg

Why’s the circle so tiny and up there on the left? and where’s the rest of it?

Let’s get a closer look at it, by making our SVG fill 100% of the browser’s height:

<svg xmlns="http://www.w3.org/2000/svg" height="100%">
        <circle r="4"/>
      </svg>
      
My-Cool-Drawing.svg

Huh, that didn’t help at all.

Alright, it’s time to add the most marvellous and powerful SVG attribute, the one that puts the ‘scalable’ into vector graphics, the mighty viewBox!

An SVG file is a partial view of the potentially infinite canvas that you can think of as ‘SVG World’. When we told the browser to fill the screen (without any further instructions), it put as much of SVG World into view as it could, which may have been a few hundred or a couple of thousand pixels worth (one pixel per SVG unit). So our circle only made up 4 pixels, a tiny fraction of that.
If we just want to view a particular section of SVG World (trust me, we do) then we can use a viewBox to do so. It’s an attribute that you should spend a bit of time with and get to know, because it’s your camera into SVG World - and just like a camera, you can move it around, pan and zoom wherever you like. A viewBox can even crop your image to different aspect ratios.
OK, let’s determine the size and shape of the box through which we view our SVG, and the specific part of Infinite SVG World that we want to look at. The viewBox has four values:

  1. x value of the top left corner
  2. y value of the top left corner
  3. width of the viewBox
  4. height of the viewBox

0,0 seems an appropriate place for our image to start, and let’s make the size of our image 20 units across, and 20 units down.
Add a viewBox property to the opening <svg> tag with the value "0 0 20 20":

<svg
        xmlns="http://www.w3.org/2000/svg"
        height="100%"
        viewBox="0 0 20 20"
      >
      
My-Cool-Drawing.svg

Finally! Now we can start to see what’s going on.

OK, I’m going to lend you a bunch of SVG code to help us see this more clearly. Don’t worry about understanding this code for now - it adds a grid of dots and labelled x & y axes to your drawing. We’ll just use it temporarily as a visual guide to help you get used to the SVG coordinate system, and delete it later. Paste it into your SVG straight after the opening <svg> tag, and before your <circle>.

  <!-- SVG Coordinates Guide -->
        <defs><pattern id="dots" x="0" y="0" width="0.05" height="0.05">
        <circle r="0.05" cx="0.5" cy="0.5" fill="#ccc"/></pattern></defs>
        <path d="M0,0h20v20h-20z" fill="#eee"/>
        <path d="M0.5,0.5h20v20h-20z" fill="url('#dots')"/>
        <path stroke="blue" stroke-opacity="0.5" stroke-width="0.05" fill="none"
          d="M-1,0h6v-0.5m0,0.5h5v-0.5m0,0.5h5v-0.5m0,0.5h5v-0.5
          M0,-1v6h-0.5m0.5,0v5h-0.5m0.5,0v5h-0.5m0.5,0v5h-0.5"/>
        <g font-family="monospace" font-size="0.5px" fill="gray">
        <g text-anchor="middle">
        <text x="5" y="-1">5</text><text x="10" y="-1">10</text>
        <text x="15" y="-1">15</text><text x="20" y="-1">20</text></g>
        <text y="5.1" x="-1.5">5</text><text y="10.1" x="-1.5">10</text>
        <text y="15.1" x="-1.5">15</text><text y="20.15" x="-1.5">20</text></g>
        <!-- ⬇️ Your drawing starts here ⬇️ -->
      

The other thing that we can do to understand what the viewBox did for us, is that we can style our SVG with a 1px red border in order to reveal the viewBox:

<svg
        xmlns="http://www.w3.org/2000/svg"
        height="100%"
        viewBox="0 0 20 20"
        style="border: 1px red solid;"
      >
      
51015205101520

Now you should see the dot grid, and a red outline around your SVG. Let’s change the viewBox values to get a better view. We’re essentially ‘zooming out’ by 20% here, so that the top left corner starts at -2,-2 and we’re now viewing a 24×24 unit section of SVG World.

<svg
        xmlns="http://www.w3.org/2000/svg"
        height="100%"
        viewBox="-2 -2 24 24"
        style="border: 1px red solid;"
      >
      

Now we can see the labelled axes, you will see that the top left corner of the viewBox is at -2,-2 and that it is 24 units wide and high, i.e. it stretches from -2 to 22 on both axes. Our ÄĽeight attribute that we set earlier ensures that those 24 units are scaled to fit the height of the screen.
Play around with the viewBox values to see what they do, checking the result in the browser:
Make it wide. Make it tall and skinny. Try to make the viewBox show only the bottom right quarter of the grid. Zoom in. Zoom out.
Got the hang of the viewBox now? …kinda? good enough, onwards!

p.s. don’t forget to set your viewBox back to “-2 -2 24 24” when you’re done experimenting.

Although we told the browser to draw a circle with a radius of 4, we didn’t tell it where we wanted it, or what color it should be. So it did what it thought was most helpful, and drew it centred on 0,0 with a black fill. Anytime that you don’t specify positions or colors, the browser (who has some weird gothy OCD tendencies) will paint things black and stack them carefully in the corner.
So let’s move this circle elsewhere and give it some color. A <circle> element is positioned with a ‘center X’ (cx) and ‘center Y’ (cy) value, which… determine where its center will be.
This circle is going to be our sun, so let’s make its fill color LightYellow.

  <circle r="4" cx="5" cy="5" fill="LightYellow" />
      

Now we’re getting somewhere! Let’s draw our picture!

In order to explore a couple more SVG elements, we’re going to draw a house. Let’s start with the main structure, which will be a rect (rectangle) element. The x and y value determine the position of the top left corner, and I’m going to let you figure out what width and height do.

<rect x="11" y="10" width="5" height="4" fill="Peachpuff"/>

Our house needs a roof, so we’ll use a three-sided polygon - but it’s actually more useful to think of it as a three-pointed polygon. We want our roof to start just to the left of our house, at 10,10, then go up to a central peak at 13.5,7, then it would be nice and symmetrical if the third point was 17,10. So we’ll use those coordinates as the points for our polygon, and the triangular shape between those three points will be filled with a delicious Tomato color. Your roof doesn’t have to be triangular, by the way. A polygon can be as many points as you like, just keep chucking more in there, it will be fine. Build a full-blown McMansion roof if you’re feeling extravagant. I’ll stick with my little tomato triangle though.

<polygon points="10,10 13.5,7 17,10" fill="Tomato"/>
      

Note: the colors I’m using are chosen from the list of Named HTML Colors, which is a pretty haphazard collection of unevenly distributed colors with sometimes terrible names. Many were chosen and named by software developers in the '80s holding up crayons next to the screen. Some may have been chosen by monkeys flinging poop. They don’t make much sense from any perspective of color science or artistic color theory. But it’s a lot easier to remember the name Peachpuff than ffdab9 or rgb(255, 218, 185) so they’re useful when you’re quickly ‘sketching’ with code like this.

Now we’ve got three shapes in our image, and they’re all different elements, so it’s pretty easy to guess which shape in the code corresponds to which shape in the drawing. But as we add more shapes, this is going to get a lot more confusing. Try to be kind to anybody who might read your code (yourself included) and add an id to each shape. That way you can quickly scan your file and find the part you’re looking for. An id will also come in handy later, when we start animating.

<circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
      <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
      <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>
      

Let’s pile on the last few shapes for our house - a door, a window, and a chimney.

<circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
      <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
      <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>

      <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
      <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
      <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
      

If you look at our image now, you might see that there’s a problem. The chimney sits in front of the roof, but it would look a lot tidier if it were behind the roof. It’s good to remember that when a browser reads SVG, it will draw elements in the order it reads them. So elements written earlier in the file will be drawn first. Anything that comes after will be drawn on top of them.

Note: This is a bit counter-intuitive for people used to using desktop graphics applications like Krita, Glimpse or Photoshop, where layers that are higher are closer to the viewer. But you’ve already seen that the coordinates in SVG go downwards too, so you’ll just have to get used to everything being upside down 🙃 in SVG World.

Where were we? ah, yes, the chimney. Cut it from its current position, and paste it directly before the roof in the code. That will put it behind the roof in the image:

<circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
      <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>

      <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
      <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>

      <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
      <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
      

Our house is now getting more complicated, it is made of of 5 shapes now. Which is unfortunate, as, for complex artistic reasons known only to me, I have decided that it needs to be a bit bigger, and in a slightly different place. Which means we’re going to have to change 4 values in each <rect>, and the three sets of coordinates in the <polygon>, and do a bunch of annoying maths in our heads.

If only there was a better way!
Of course there is, you silly sausage. We’ll use a group! We can wrap all those house elements within <g></g> tags, and then move or scale the group! We can apply a transform to both translate and scale the group.

<g id="house" transform="scale(1.2) translate(-2,-1)">
        <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
        <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
        <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>
        <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
        <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
      </g>
      

Now we can just add some grass and some sky, trim the viewBox to the actual size of our image, and delete the Coordinates Guide (you don’t need it anymore, I’m so proud of you 💐️😢️)
…and… our image is done!

<svg xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 0 20 20" >

        <rect id="sky" width="20" height="20" fill="SkyBlue"/>
        <circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
        <rect id="grass" width="20" height="7" y="13" fill="MediumSeaGreen"/>

        <g id="house" transform="scale(1.2) translate(-2,-1)">
          <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
          <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
          <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>
          <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
          <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
        </g>

      </svg>
      

So far we have been adding attributes inline, that is, directly writing the fill color and the transforms and all the rest within an element’s tag.

When it comes to adding animation to our SVG however, there’s a whole lot more extra information to include - things would definitely get messy if we were to keep everything inline. So we’ll keep our animation code confined to a <style> section. This can be placed anywhere in an SVG file, but the convention is to put it at the start of the file, before our drawing.

<svg xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 0 20 20" >
        <style>
          /* 🚴🏻‍💨️ Animation details go here 💓 */
          /* anything within these style tags is written in CSS syntax, not SVG/XML */
        </style>
        <rect id="sky" width="20" height="20" fill="SkyBlue"/>
        <circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
        ...
      

So far we have been writing SVG, but now we’re going to switch it up and write some CSS inside our SVG. But as you’ll see it’s not that different really. Let’s use CSS to move the sun in our image downwards, so its center is below the horizon.
We use #sun as the selector, which means ‘the element with an id of sun’. That goes at the start of a new line, and then we open some curly braces, in which we will declare the specific styling we want to apply to #sun. In our case, we want a translate transform, which operates just like the SVG transform we wrote earlier, it just has slightly different punctuation.

  <style>
          #sun {
            transform: translate(0, 10px);
          }
        </style>
      

How come I wrote ‘px’ after 10?
So far we’ve been getting along fine without any units, why ruin a good thing now?
Well, SVG’s a bit more relaxed about units than most programming languages. It’s all, like “just create, man, just feel it - don’t let the metric system get you down, follow your own coordinate system, you know? deal with display sizes later…”
But CSS’ whole existence is based on rules. CSS is sick of tidying up SVG’s shit, and they really need SVG to just grow up and pick an explicit unit for once in their life.
So when SVG is talking to CSS it pretends that it’s following the nice, regimented, px-based system that CSS likes, and accepts rules like ‘translate 10px’ - “oh, sure thing, boss…” but the SVG is actually following its own coordinate system internally, because it’s not going to let the Man get in its head.
Thanks to the 20×20 viewBox and the height of 100%, in this case, CSS’ 10px doesn’t mean 10 pixels on the screen, it means half the height of the image, and the image might be displayed as ~1000px high, depending on the screen it’s viewed on. If the sun element was sitting in a group with a scale transform, that would mess with the number of actual screen pixels translated even further.
So the moral of the story is, when CSS says “move the sun by 10px” to an SVG, it doesn’t mean 10 pixels on the screen. It means 10 SVG units in whatever context and coordinate system the object might be. “It’s all, like, relative, man.”

How come I didn’t write ‘px’ after 0?
Well, zero is CSS’ happy place. Zero is Zero is Zero. Nobody ever breaks that rule.
Zero pixels = Zero nautical miles = Zero feet = Zero nanometers = Zero banana emojis. Everything is just so clear, consistent and unambiguous. All is well with the world. This is the only time that CSS really just relaxes and lets its hair down. What?! You want to write a distance without units? HOW DARE Y— Wait, is it... zero?
Oh, well that’s no problem at all, friend, just go right ahead.

I hope you enjoyed that in-depth psychological analysis of the the fraught relationship between CSS and SVG, but I realise that you may have other concerns, like “why doesn’t this picture move? I was promised animation, instead the sun is just in a different place. What is this shit?”
Ok, we’ll make it move then. jeez.

Hang on to that transform: translate(0, 10px); declaration, we’re going to use it in the animation soon.
But first we’ll need a name for our animation: sunset seems appropriate. Let’s also give it a duration of 4 seconds.

  #sun {
          animation-name: sunset;
          animation-duration: 4s;
        }
      

But sunset doesn’t mean anything. Yet. We need to declare what actually happens. Below the #sun ruleset we’ll write some @keyframes which will determine what properties change during the animation, how much and at what point they change. As we’re just going from one position to another, we only need two keyframes: from and to:

  #sun {
          animation-name: sunset;
          animation-duration: 4s;
        }

        @keyframes sunset {
          from {
           transform: translate(0, 0);
          }
          to {
           transform: translate(0, 10px);
          }
        }
      

Wa-hey! it’s animated! But it only runs once, in one direction, and how come it’s slow at the start and end?

Let’s deal with these one at a time. You can set the number of times an animation will play using animation-iteration-count: a value of 3 will play 3 times, 1.5 will stop halfway through the second run, etc. We want this sun to be rising and setting, back and forth forever, so we’ll loop it with a value of infinite.

  #sun {
          animation-name: sunset;
          animation-duration: 4s;
          animation-iteration-count: infinite;
        }
      

We don’t just want our sun to set and then suddenly jump back into position at the end of the animation - instead we want the sun to smoothly return once it has set. To do this, we can set animation-direction to alternate.

  #sun {
          animation-name: sunset;
          animation-duration: 4s;
          animation-iteration-count: infinite;
          animation-direction: alternate;
        }
      

Finally, let’s have a look at animation-timing-function or what’s often referred to as ‘easing’ or ‘spacing’.
In the physical world, if you were to move an object with perfectly even or ‘linear’ timing, maintaining a constant speed from the first millisecond to the last, it would look and feel very unnatural, almost robotic. It would also be basically impossible, because in the physical world, things have mass and momentum, energy and friction.
If you flick a ping-pong ball, its acceleration will seem quite instantaneous (it’s not) whereas if you try to push a boulder, it will take a few seconds of sweating and grunting before it picks up any speed.
So although a linear timing function is the most mathematically simple, most of the time animators tend to use some ‘easing’ to give a more natural sense of movement. If something is shooting in from off-screen and coming to a stop, you would choose an ease-out timing function, so that the end of the animation (the ‘out’) is gradually slowed (eased). If something is exiting the screen, you may prefer an ease-in function, which means the start (in) of the animation is slower, gradually picking up speed and then maintaining constant speed near the end, to indicate that it will continue moving once off-screen.
The default timing function in CSS is something called ease which should look kinda okay for most animations, I guess. It has a slight ease-in combined with a long ease-out, and that’s what our sunset animation is showing right now.
For our animation, however, out animation will play forwards and reversed, over and over again, so we want the timing function to be symmetrical - we’ll use ease-in-out which combines a slow start with an equivalent slow end.

  #sun {
          animation-name: sunset;
          animation-duration: 4s;
          animation-iteration-count: infinite;
          animation-direction: alternate;
          animation-timing-function: ease-in-out;
        }
      

Note: Most of the time I use the animation shorthand instead of writing out all of these properties long-form. You can just chuck all of these values in row like this - as long as you’re not using a delay, the order doesn’t matter at all:

  #sun { animation: sunset 4s infinite alternate ease-in-out; }
      

OK, the sun sets and returns! But it would be great if the light changed in our scene when the sun drops behind the horizon. let’s add…

When the sun sets, we’re going to make the sky pinker, and the overall scene dark-bluer. We’re going to need two new rect elements:

  <rect id="sky" width="20" height="20" fill="SkyBlue"/>
        <!-- the Pink rect will appear over the sky -->
        <rect width="20" height="20" fill="Pink"/>
        <circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
        <rect id="grass" width="20" height="7" y="13" fill="MediumSeaGreen"/>
        <g id="house" transform="scale(1.2) translate(-2,-1)">
          <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
          <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
          <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>
          <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
          <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
        </g>
        <!-- the MidnightBlue rect will appear over the whole image -->
        <rect width="20" height="20" fill="MidnightBlue"/>

      

Well now we can’t see anything. What we need to do is to change the blending mode of our MidnightBlue rectangle - you may or may not be familiar with the concept of blending modes from desktop graphics applications, but it’s a way of determining how one element will mix, cover, affect or intersect with the elements below it. In our case, we want the hue of the shapes in our image to become more MidnightBlue. There are native blend modes in SVG but they’re kind of complicated, so the CSS developers reworked them in CSS, making them much simpler to write and understand. So we’re going to use the CSS syntax inline here, by putting the ruleset in a style attribute:

<rect width="20" height="20" fill="MidnightBlue" style="mix-blend-mode: hue;">

Now the scene looks suitably dusky.

We want to animate both our Pink and MidnightBlue rectangles at the same time, in the same way, so rather than address them by id, as we have been doing, I’m instead going to add the same class to both elements.

  <rect class="dark" width="20" height="20" fill="Pink"/>
        ...
        <rect class="dark" width="20" height="20" fill="MidnightBlue"
          style="mix-blend-mode: hue;"/>
      

Then we can easily write CSS rulesets that apply to multiple elements, even if they’re different types of element. In the <style> section, below our sunset keyframes, let’s add a new ruleset, selecting the ‘dark’ class with the . symbol. I want this to be in sync with the sun, so I’ll keep everything but the animation-name consistent:


        
          .dark { animation: darken 4s infinite alternate ease-in-out; }
        
      

We’ll also define the darken animation using @keyframes:

@keyframes darken {
        from {
          opacity: 0;
        }
        to {
          opacity: 0.9;
        }
      }
      

Note: You can also use 0% and 100% instead of from and to - for animations that are more complex than simply transitioning from one state to another, you may want to also add in another keyframe at 40%, and another at 60%, etc.

Finished! We now have a beautiful sunset image, which loops forever, and ever, and ever…

You can now use this in a website! For maximum scalabily and flexibility, I would recommend removing the height attribute we included in the <svg> tag and instead setting the size of the image in the HTML/CSS, so that you can adjust it based on your layout.
There are a few ways that you can include the SVG, eg. by including it in an <img> tag in your HTML, like so:

<!DOCTYPE html>
      <html>
        <head>
          <style>
            img {
              width: 60vw;
              margin: auto;
            }
          </style>
        </head>
        <body>
          <h1>Woah, Check Out This Sweet Animation</h1>
          <img
            alt="animation of a sunset behind a little house"
            src="../images/My-Cool-Drawing.svg"
          >
        </body>
      </html>
      

Or, you could include the SVG inline in the HTML, which is what I would recommend if you want to do anything interactive and javascripty with it:

<!DOCTYPE html>
      <html>
        <head>
          <style>
            svg {
              width: 60vw;
              margin: auto;
            }
          </style>
        </head>
        <body>
          <h1>Woah, Check Out This Sweet Animation</h1>

          <svg viewBox="0 0 20 20" >
            <title>My Cool Drawing</title>
            <desc>animation of a sunset behind a little house</desc>

            <style>
              #sun { animation: sunset 4s infinite alternate ease-in-out; }
              @keyframes sunset {
                from { transform: translate(0, 0); }
                to { transform: translate(0, 10px); }
              }

              .dark { animation: darken 4s infinite alternate ease-in-out; }
              @keyframes darken {
                from { opacity: 0; }
                to { opacity: 1; }
              }
            </style>

            <rect id="sky" width="20" height="20" fill="SkyBlue"/>
            <rect class="dark" width="20" height="20" fill="Pink"/>
            <circle id="sun" r="4" cx="5" cy="5" fill="LightYellow" />
            <rect id="grass" width="20" height="7" y="13" fill="MediumSeaGreen"/>
            <g id="house" transform="scale(1.2) translate(-2,-1)">
              <rect id="housefront" x="11" y="10" width="5" height="4" fill="Peachpuff"/>
              <rect id="chimney" width="1" height="2" x="15" y="8" fill="Chocolate"/>
              <polygon id="roof" points="10,10 13.5,7 17,10" fill="Tomato"/>
              <rect id="door" width="1" height="2.5" x="12" y="11.5" fill="SteelBlue"/>
              <rect id="window" width="1" height="1" x="14" y="11" fill="SteelBlue"/>
            </g>
            <rect class="dark" width="20" height="20" fill="MidnightBlue"
              style="mix-blend-mode: hue;"/>
          </svg>

        </body>
      </html>
      

Notes: