How to Create Pure CSS Games

How I Built a Pure CSS Game — No JS

Elad Shechter
May 3, 2020 · 8 min read

The dynamic of the internet is associated most of the time with JavaScript language. But it has been quite a while since CSS has been able to provide you with some of those possibilities.

In order to test these possibilities, I have decided to create my own Pure CSS Game — Coronavirus Invaders. In this article, I will teach you how to create those things you thought you could only create with JavaScript. I will do so by demonstrating these capabilities through my pure CSS game.

Note: Some of these CSS tricks are possible with CSS, but from a perspective of semantics and accessibility, it doesn’t mean that I recommend to use them on a daily basis.

Creating Functional Main Navigation

In order to create a functional navigation, I used the upper checkbox trick.

  1. In this trick, I have inserted a few checkboxes at the beginning of the HTML. These checkboxes represent the primary states of the game. To hide them visually, I have styled them with with and . Besides that, I gave each one of them a unique ID in order to control them, for example: .
  2. The visual buttons of the game are elements with an attribute of . The value of the attribute is connected to the unique ID attribute of the checkbox in section 1. Now, when you click on one of the elements it will check/uncheck the connected checkbox. In this way, I can target the checkbox, and save states.
  3. In CSS, I can check with the pseudo selector- , if a checkbox is checked. And with the sibling selector of I can target any sibling element (and even every child of those sibling elements) and decide if to show or hide them.
  4. In the same way (of section 3) the Start Playing (“Save the World”) is a trigger checkbox, which the main screen, and the frame of the game.

HTML

<input class="logic-checkbox" type="checkbox" id="toggleGame">
<input class="logic-checkbox" type="checkbox" id="HowToPlayPopup">
<input class="logic-checkbox" type="checkbox" id="whoAmIPopup">
<!--game navigation-->
<section class="game-menu-frame">
<ul class="game-nav-list">
<li class="game-nav-item">
<label for="toggleGame" tabindex="0">Save the World</label>
</li>
<li class="game-nav-item">
<label for="HowToPlayPopup" tabindex="0">How to Play</label>
</li>
<li class="game-nav-item">
<label for="whoAmIPopup" tabindex="0">Who Am I</label>
</li>
</ul>
</section>
<section class="game-frame">
<!--game frame-->
</section>
<section class="popup common-content" id="whoAmI">
<!-- Who Am I popup -->
</section>
<section class="popup common-content" id="HowToPlay">
<!-- How to play popup -->
</section>

CSS (Scss)

/* checkbox which triggering popup */
#whoAmIPopup:checked ~ #whoAmI,
#HowToPlayPopup:checked ~ #HowToPlay{
display: block;
}
/* checkbox of start playing -
trigger by pressing "save the world" button */
#toggleGame:checked {
~ .game-menu-frame{ display: none; }
~ .game-frame{ display: block; }
}
/* hide popup and game frame */
#whoAmI,
#HowToPlay,
#game-frame{display: none;}

Playable CodePen Example

Killing Coronavirus

The floating Coronavirus in this game are working very similar to the — Functional Main Navigation, but with two small tweaks:

Click on Viruses disappearing them

First, every coronavirus is a floating , But instead of trigger we are triggering here . This way, when clicking on a virus, it will be in the status of checked. This checked status represents that the virus is dead. Because it’s a radio button input, we can change the state of the virus only once ( = dead).

Second, the is placed inside the . This way when we click on the element, it will immediately target the . In this case, we don’t need to give attribute for the inputs and the attribute to the . This nested connection triggers itself.

The logic of the styles is when the is , the sibling element of the corona will get . In this way, I make the Coronavirus disappear from the screen.

HTML

<label class="corona-virus">
<input type="radio">
<div class="body">
...
</div>
</label>

CSS (Scss)

/* kill corona */
input[type="radio"]{
opacity:0.001; /* hide radio */
/*if radio is checked hide virus*/
&:checked{
~ .body {opacity:0;}
}

}

Playable CodePen Example

Calculating the Score Result on Run-time

After we have pressed on some viruses while playing the game, those got . Now, we need to count all those which are . We do so using CSS Counters, which exist since CSS 2.1 version.

CSS Counter calculates radio inputs which are :checked

Let’s do it step by step:

  1. First, we define the CSS counter with the property. The value is a name that we give — it is called counter value. For it to work, it needs to be defined on HTML element which is before all the elements in our HTML. In our case: .
  2. Now we want to count all of those which are . We will create a CSS selector of and we will give it the property, with the same counter value we created in the first section. In our case: .
  3. To show the summarize of the score on the screen, we will add an HTML element, for example: , which has to be after all elements of the HTML.
  4. Now, what we have left to do is to print it on the screen, using the pseudo-element selector while using the property style.
    The CSS property can get a CSS native function called which will get the counter value, that we have already created in section 1, and increment it on section 2.
    Example: .

HTML

<body> <!-- define counter --><ul class="corona-world">
<li class="corona-location corona-virus-1-location">
<label class="corona-virus">
<input type="radio">
</label>
</li>
... <!-- 100 items of viruses -->
</ul>
<div class="sum">
<span class="text">Score:</span>
</div>
...
</body>

CSS

body{ 
counter-reset: corona;
}
input[type="radio"]:checked{
counter-increment: corona;
}
.sum::after{
content: counter(corona) "/100";
}

Playable CodePen Example

Creating the Countdown Clock

In this example, I create the countdown with CSS Animation and with the pseudo-elements & . Every pseudo-element represents one of the numbers of the countdown. The pseudo-element represents the counter of every ten seconds, and the second number represents every second. This way, I can create a counter that counts from 99 to 0 seconds. I gave each one of the pseudo-elements an animation on the property value, that runs from 9 to 0.

CSS Pure Countdown

They both got the same animation but with a different timeline and different amount of iterations. The is iterating one time for 100 seconds. The is iterating ten times of 10 seconds each. In this way, It looks like the counter is running from 99 to 0.

For it to work well, I used the property with a value of , which creates precise jumping of the seconds. The second important thing is to use the property with the value of . This way, when the animation will end, it will remain at the end.

HTML

<div class="count-down">
<span class="icon">⏲️</span>
<span class="count">
::before (pseudo element)
::after (pseudo element)
</span>
</div>

CSS

.count-down{
.count{
&::before{
content: "";
animation:countdown 100s step-end 1 forwards;
}
&::after{
content: "";
animation: countdown 10s step-end 10 forwards;
}
}
}
@keyframes countdown {
0% { content: "9" }
10% { content: "8" }
20% { content: "7" }
30% { content: "6" }
40% { content: "5" }
50% { content: "4" }
60% { content: "3" }
70% { content: "2" }
80% { content: "1" }
90%, 100% { content: "0" }
}

Note: CSS step animation function isn’t supported in the Safari browser.

Playable CodePen Example

Creating a Visual Timer

Because of the lack of support of the regular countdown in Safari, I created another visual timer, which will show as well.

Pure CSS Visual Timer

This timer has fixed and . To show the status of the timeline, I used the pseudo-element , which will update in with the animation property according to the time that has passed.

HTML

<div class="timer">
::before
</div>

CSS

.timer {
width: 150px;
height: 10px;
background-color: #fff;
border: solid 1px #333;
&::before{
content: "";
width: 0px;
height: 10px;
display: block;
background-color: #ccc;
animation: timer 100s linear 0s;
animation-fill-mode: forwards;
}
}
@keyframes timer{
0%{width: 3%;}
85%{width: 85%; background-color: #ccc;}
90%{width: 90%; background-color: red;}
100%{width: 100%; background-color: red;}
}

Playable CodePen Example

Schedule the Game-Over Curtain

The game-over curtain is placed inside the with and and . The animation of the curtain updates the styles to and .

Schedule Game Over Curtain

Now, the trick here is to give the curtain according to the time of the game, for example: . This way, the curtain will appear only after 100 seconds. Besides, we will give the animation because we are aiming that when the animation ends, it will remain at the end of the timeline.

HTML

<section class="game-frame">...HTML of Viruses
...HTML of Countdown & Timer
<section class="game-over">
<h2 class="game-title">Game Over</h2>
</section>
</section>

CSS

.game-over{
height: 0;
overflow: hidden;
opacity: 0;
position:fixed; z-index:20;
left:0; right:0; top:0;
background-color: rgba(0, 0, 0, 0.6);animation: curtain 0.6s ease-in;
animation-delay: 100s;
animation-fill-mode: forwards;

}
@keyframes curtain {
from{height:100vh; opacity:0;}
to {height:100vh; opacity:1;}
}

Playable CodePen Example

To Summarize

In this article, I demonstrated the most important tips & tricks for how to create pure CSS games by breaking apart most of its logic.

My new talk on How to Create Pure CSS Games

This is my full talk on how to create Pure CSS Games, fill free to share it 🙂

Final Words

I hope that in this article, I gave you some inspiration on how to create pure CSS games, and maybe even inspire you to create your own pure CSS game.

If you like this article, I would appreciate applause and sharing 🙂.

You can follow me via Twitter and Linkedin.
Besides, you can find more of my content on my website — eladsc.com.

Who Am I?
I am Elad Shechter, a Web Developer specializing in CSS & HTML design and architecture.

My Full Pure CSS Game — Coronavirus Invaders CodePen:

Coronavirus Invaders — CSS Pure Game (No JS!)

Get more content like this

Signup if you find it interesting 🤓
Articles, open-source projects, and deep-dive into CSS topics.

cssclass.com

A CSS community by Anima.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store