{"id":2936,"date":"2024-07-08T08:34:58","date_gmt":"2024-07-08T14:34:58","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=2936"},"modified":"2024-07-08T08:34:59","modified_gmt":"2024-07-08T14:34:59","slug":"how-keyboard-navigation-works-in-a-css-game","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/how-keyboard-navigation-works-in-a-css-game\/","title":{"rendered":"How Keyboard Navigation Works in a CSS Game"},"content":{"rendered":"\n<p>We&#8217;re going to build a &#8220;Super CSS Mario&#8221; game where you can use a keyboard and the arrow keys to move Mario around. <a href=\"https:\/\/codepen.io\/t_afif\/full\/OJYbVWP\">Go ahead and play it<\/a> to check it out. Note there is no JavaScript at all, it&#8217;s just HTML and CSS.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='343' src='https:\/\/videopress.com\/embed\/7wEmf0lx?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<h2 class=\"wp-block-heading\">Why bother with a CSS-only game?<\/h2>\n\n\n\n<p>Creating a CSS-only game is a fun exercise. Restricting yourself to only HTML &amp; CSS allows you to discover and unlock CSS trickery you can add to your toolbox. That&#8217;s what happens for me! <\/p>\n\n\n\n<p>You might think that limiting yourself to CSS for all the functionality of the game is useless. CSS is not designed for this sort of thing, it&#8217;s for layouts and design control. But doing unusual and unexpected things in CSS is a great way to practice, and will lead to a deeper understanding of the language, making you a better CSS developer all around.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Interactivity without a Mouse<\/h2>\n\n\n\n<p>Many pure CSS games you will see around are playable mostly with a mouse. They rely on interactive elements such as checkboxes and pseudo-classes like\u00a0<code>:hover<\/code>,\u00a0<code>:active<\/code>, <code>:checked<\/code>, and so on. But with recent CSS features, a <em>keyboard<\/em> control game (beyond tabbing) is also doable using CSS!<\/p>\n\n\n\n<p>Cool right? Stay with me if you want to know the secret behind creating this game (and a few others at the end).<\/p>\n\n\n\n<p class=\"learn-more\">At the time of writing this, only Chrome (and Edge) have the full support of the features we will be using so consider those browsers to read the article.<\/p>\n\n\n\n<p>For the sake of simplicity, I will skip the aesthetic parts. I will mainly focus on the techniques used to build the game. The code of demos used in the article may differ slightly from the real code used in the game.<\/p>\n\n\n\n<p>Let\u2019s start with this basic setup:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_bGyPMar\/9232383db3533eebbb0ac04c69930278\" src=\"\/\/codepen.io\/anon\/embed\/bGyPMar\/9232383db3533eebbb0ac04c69930278?height=450&amp;theme-id=47434&amp;slug-hash=bGyPMar\/9232383db3533eebbb0ac04c69930278&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed bGyPMar\/9232383db3533eebbb0ac04c69930278\" title=\"CodePen Embed bGyPMar\/9232383db3533eebbb0ac04c69930278\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We have a container with an overflowing content that will trigger both the vertical and horizontal scrolling. Nothing fancy so far but let\u2019s not forget that, in addition to the mouse, we can scroll the container using the direction keys. Try it! Click the container (above) then use the keyboard to scroll inside it.<\/p>\n\n\n\n<p>Now let\u2019s add two more elements inside the overflowing div to have the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then we make the&nbsp;<code>.game<\/code>&nbsp;element <code>sticky<\/code> so it doesn\u2019t move when we scroll the container:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ExzBLLJ\/bf04b6fc98e2693aa1a21e07129669ac\" src=\"\/\/codepen.io\/anon\/embed\/ExzBLLJ\/bf04b6fc98e2693aa1a21e07129669ac?height=450&amp;theme-id=47434&amp;slug-hash=ExzBLLJ\/bf04b6fc98e2693aa1a21e07129669ac&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ExzBLLJ\/bf04b6fc98e2693aa1a21e07129669ac\" title=\"CodePen Embed ExzBLLJ\/bf04b6fc98e2693aa1a21e07129669ac\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The magic touch now is to introduce scroll-driven animations to move our character. We can scroll the <em>outer<\/em> container but <em>everything else<\/em> stays fixed. By adding scroll-driven animations we can control the movement of Mario as we want.<\/p>\n\n\n\n<p>It may sound tricky, but the code is pretty simple:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.mario<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">0%<\/span>;\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">0%<\/span>;\n  <span class=\"hljs-attribute\">animation<\/span>: \n    x linear,\n    y linear;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: \n    <span class=\"hljs-built_in\">scroll<\/span>(nearest inline),\n    <span class=\"hljs-built_in\">scroll<\/span>(nearest block);\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> x { <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">100%<\/span> } }\n<span class=\"hljs-keyword\">@keyframes<\/span> y { <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">100%<\/span>  } }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We have two animations. Each controls the movement in one direction (horizontal or vertical). Then we link them with the scrolling of the outer container. The&nbsp;<code>x<\/code>&nbsp;animation will follow the \u201cinline\u201d scroll (horizontal) and the&nbsp;<code>y<\/code>&nbsp;animation will follow the \u201dblock\u201d scroll (vertical).<\/p>\n\n\n\n<p>In other words, the scrolling will define the progress of the animation. Try it:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XWwLqPG\/2bacc195d64bd482ace47966d3a6e49e\" src=\"\/\/codepen.io\/anon\/embed\/XWwLqPG\/2bacc195d64bd482ace47966d3a6e49e?height=450&amp;theme-id=47434&amp;slug-hash=XWwLqPG\/2bacc195d64bd482ace47966d3a6e49e&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XWwLqPG\/2bacc195d64bd482ace47966d3a6e49e\" title=\"CodePen Embed XWwLqPG\/2bacc195d64bd482ace47966d3a6e49e\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We have our keyboard control! <\/p>\n\n\n\n<p>We can still use the mouse to manipulate the scrollbars but if we hide them, the illusion is perfect!<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.container<\/span> {\n  <span class=\"hljs-attribute\">scrollbar-width<\/span>: none;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_zYQVjmX\/6d9b6d3a8c64c00c1a91795789db49b2\" src=\"\/\/codepen.io\/anon\/embed\/zYQVjmX\/6d9b6d3a8c64c00c1a91795789db49b2?height=450&amp;theme-id=47434&amp;slug-hash=zYQVjmX\/6d9b6d3a8c64c00c1a91795789db49b2&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed zYQVjmX\/6d9b6d3a8c64c00c1a91795789db49b2\" title=\"CodePen Embed zYQVjmX\/6d9b6d3a8c64c00c1a91795789db49b2\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>You still need to click inside the container to get the focus before using the keyboard. I add the&nbsp;<code>tabindex<\/code>&nbsp;attribute to the main container so you can get the focus using the \u201ctab\u201d key as well.<\/p>\n\n\n\n<p>The game can be playable using only the keyboard. Here is&nbsp;<a href=\"https:\/\/css-games.com\/super-css-mario\/\">the link for the full game<\/a>&nbsp;again to test it. Either use the mouse or click \u201ctab\u201d to start the game.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"adding-the-coins\">Adding The Coins<\/h2>\n\n\n\n<p>Could we add coins on the screen and then have Mario &#8220;collect&#8221; them when they touch? Well&#8230; no, not really. CSS does not have &#8220;collision detection&#8221; (yet?). So let&#8217;s fake it!<\/p>\n\n\n\n<p>Since we&#8217;re controlling the location of Mario with animations, we can know where he is located. We are going to rely on this information to simulate collision detection between Mario and the coins.<\/p>\n\n\n\n<p>To start, let\u2019s place a coin inside the game board:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><mark class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/mark><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And style it like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.coin<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">inset<\/span>: <span class=\"hljs-number\">0<\/span>;\n}\n<span class=\"hljs-selector-class\">.coin<\/span><span class=\"hljs-selector-pseudo\">:before<\/span> {\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"\"<\/span>;\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">50px<\/span>;\n  <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">25px<\/span>);\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">50%<\/span> - <span class=\"hljs-number\">25px<\/span>);\n  <span class=\"hljs-attribute\">aspect-ratio<\/span>: <span class=\"hljs-number\">1<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The&nbsp;<code>.coin<\/code>&nbsp;container will fill the whole area of&nbsp;<code>.game<\/code>&nbsp;(we will see later why) and its pseudo-element is the visible coin:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_MWdMMwK\/e6aeebe23b40327255155c462f8a52be\" src=\"\/\/codepen.io\/anon\/embed\/MWdMMwK\/e6aeebe23b40327255155c462f8a52be?height=450&amp;theme-id=47434&amp;slug-hash=MWdMMwK\/e6aeebe23b40327255155c462f8a52be&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed MWdMMwK\/e6aeebe23b40327255155c462f8a52be\" title=\"CodePen Embed MWdMMwK\/e6aeebe23b40327255155c462f8a52be\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>The coin is placed at the center and to reach the center Mario needs to scroll half the distance vertically and horizontally which means it needs to reach half the&nbsp;<code>x<\/code>&nbsp;and&nbsp;<code>y<\/code>&nbsp;animations.<\/p>\n\n\n\n<p>We use this information to define new animations that we link to the coin element like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.coin<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: \n    c-x linear,\n    c-y linear;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: \n    <span class=\"hljs-built_in\">scroll<\/span>(nearest inline),\n    <span class=\"hljs-built_in\">scroll<\/span>(nearest block);\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> c-x {\n  0% , 44%  {<span class=\"hljs-attribute\">--c-x<\/span>: <span class=\"hljs-number\">0<\/span>}\n  45%, 55%  {<span class=\"hljs-attribute\">--c-x<\/span>: <span class=\"hljs-number\">1<\/span>}\n  56%, 100% {<span class=\"hljs-attribute\">--c-x<\/span>: <span class=\"hljs-number\">0<\/span>}\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> c-y {\n  0% , 44%  {<span class=\"hljs-attribute\">--c-y<\/span>: <span class=\"hljs-number\">0<\/span>}\n  45%, 55%  {<span class=\"hljs-attribute\">--c-y<\/span>: <span class=\"hljs-number\">1<\/span>}\n  56%, 100% {<span class=\"hljs-attribute\">--c-y<\/span>: <span class=\"hljs-number\">0<\/span>}\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is the same animation configuration we used for Mario. One animation is linked to the horizontal scroll and another one is linked to the vertical scroll. Each animation will control a variable that will be either&nbsp;<code>0<\/code>&nbsp;or&nbsp;<code>1<\/code>&nbsp;based on the&nbsp;<code>keyframes<\/code>&nbsp;percentage.<\/p>\n\n\n\n<p>The coin is placed at the center so we need the variable to turn&nbsp;<code>1<\/code>&nbsp;when the animation is around&nbsp;<code>50%<\/code>. I am considering an offset of&nbsp;<code>5%<\/code>&nbsp;to illustrate the idea but in the real code, I am using more accurate values.<\/p>\n\n\n\n<p>Now, we will introduce another CSS feature: style queries. It allows us to conditionally apply specific CSS based on the value of custom properties (CSS variables). Style queries require a parent-child relation, so that\u2019s why the real coin is the pseudo element of&nbsp;<code>.coin<\/code>&nbsp;container.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.coin<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: c;\n}\n<span class=\"hljs-keyword\">@container<\/span> c style(<span class=\"hljs-attribute\">--c-x:<\/span> <span class=\"hljs-number\">1<\/span>) <span class=\"hljs-keyword\">and<\/span> style(<span class=\"hljs-attribute\">--c-y:<\/span> <span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-class\">.coin<\/span><span class=\"hljs-selector-pseudo\">:before<\/span> {\n     <span class=\"hljs-comment\">\/* do what you want here *\/<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The previous animations will make both variables equal to 1 at&nbsp;<code>50%<\/code>&nbsp;(when Mario is at the center) and the style query will apply a specific CSS when both variables are equal to&nbsp;<code>1<\/code>.<\/p>\n\n\n\n<p>In the below example, when Mario is above the coin, a red background will appear. We have our collision detection!<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_abrggpy\/edf26c1bf169b0e468214661b79e0ea4\" src=\"\/\/codepen.io\/anon\/embed\/abrggpy\/edf26c1bf169b0e468214661b79e0ea4?height=450&amp;theme-id=47434&amp;slug-hash=abrggpy\/edf26c1bf169b0e468214661b79e0ea4&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed abrggpy\/edf26c1bf169b0e468214661b79e0ea4\" title=\"CodePen Embed abrggpy\/edf26c1bf169b0e468214661b79e0ea4\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>As I said previously, this is not super accurate. I am keeping this simple to illustrate the idea.&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/OJYbVWP\">In the real code<\/a>, I am using more precise calculations to get a perfect collision detection.<\/p>\n\n\n\n<p>What we did until now is good but we need better. The red color is only visible when Mario touches the coin but we need a way to <em>maintain<\/em> this state. In other words, if it turns red, it should <em>stay<\/em> red.<\/p>\n\n\n\n<p>To achieve this, we have to introduce another animation and update the code like below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.coin<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: c;\n}\n<span class=\"hljs-selector-class\">.coin<\/span><span class=\"hljs-selector-pseudo\">:before<\/span> {\n  <span class=\"hljs-attribute\">animation<\/span>: touch .<span class=\"hljs-number\">1s<\/span> forwards linear <span class=\"hljs-built_in\">var<\/span>(--s, paused);\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> touch {\n  1%, 100% { <span class=\"hljs-attribute\">background-color<\/span>: red; }\n}\n<span class=\"hljs-keyword\">@container<\/span> c style(<span class=\"hljs-attribute\">--c-x:<\/span> <span class=\"hljs-number\">1<\/span>) <span class=\"hljs-keyword\">and<\/span> style(<span class=\"hljs-attribute\">--c-y:<\/span> <span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-class\">.coin<\/span><span class=\"hljs-selector-pseudo\">:before<\/span> {\n     <span class=\"hljs-attribute\">--s<\/span>: running\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We define an animation that is \u201cpaused\u201d initially and when the condition is met we make it \u201crunning\u201d. I am using a small duration and a&nbsp;<code>forwards<\/code>&nbsp;configuration to make sure the red color stays even when Mario moves away from the coin.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_XWwvrmL\/1bf649f565c13d56b7b36b8c3e357cae\" src=\"\/\/codepen.io\/anon\/embed\/XWwvrmL\/1bf649f565c13d56b7b36b8c3e357cae?height=450&amp;theme-id=47434&amp;slug-hash=XWwvrmL\/1bf649f565c13d56b7b36b8c3e357cae&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed XWwvrmL\/1bf649f565c13d56b7b36b8c3e357cae\" title=\"CodePen Embed XWwvrmL\/1bf649f565c13d56b7b36b8c3e357cae\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>With this configuration, we can add an animation that makes the coin disappear (instead of just the color change).<\/p>\n\n\n\n<p>To add more coins, we add more&nbsp;<code>.coin<\/code>&nbsp;elements with different positions and animations. If you check&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/OJYbVWP\">the real code of the game<\/a>&nbsp;you will find that I am defining different variables and using Sass to generate the code. I am using a grid system where I can control the number of columns and rows and I am defining another variable for the number of coins. Then with the help of the&nbsp;<code>random()<\/code>&nbsp;function from Sass I can randomly place the coins inside the grid.<\/p>\n\n\n\n<p>The important thing to notice is how the HTML code is organized. We don\u2019t do the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      ...\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But rather the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>     \n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The&nbsp;<code>.coin<\/code>&nbsp;elements should not be siblings but nested inside each other. I need this configuration to later calculate the score. For this reason, a&nbsp;<code>.coin<\/code>&nbsp;element needs to take the whole area of the game to make sure its descendants will have access to the same area and we can easily place all the coins following the same code structure.<\/p>\n\n\n\n<p>There is probably a way to make the game work by having the&nbsp;<code>.coin<\/code>&nbsp;elements as siblings but I didn\u2019t focus on the HTML structure <em>too<\/em> much.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"calculating-the-score\">Calculating The Score<\/h2>\n\n\n\n<p>To calculate the score, I will add a last element that should also be nested within all the&nbsp;<code>.coin<\/code>&nbsp;elements. The nested configuration is mandatory here to be able to query all the&nbsp;<code>.coin<\/code>&nbsp;elements.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>     \n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n           <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"result\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And here is the Sass code to illustrate how to calculate the score:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.result<\/span> {\n  $<span class=\"hljs-attribute\">anim<\/span>: ();\n  @for $i from 1 to ($c+1) {\n    --r#{$i}<span class=\"hljs-selector-pseudo\">:0<\/span>; \n    $<span class=\"hljs-selector-tag\">anim<\/span>: <span class=\"hljs-selector-tag\">append<\/span>($<span class=\"hljs-selector-tag\">anim<\/span>,'<span class=\"hljs-selector-tag\">r<\/span>#{$i} <span class=\"hljs-selector-class\">.1s<\/span> <span class=\"hljs-selector-tag\">forwards<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--s-r<\/span>#{$i},<span class=\"hljs-selector-tag\">paused<\/span>)',<span class=\"hljs-selector-tag\">comma<\/span>);\n    <span class=\"hljs-keyword\">@container<\/span> c#{$<span class=\"hljs-selector-tag\">i<\/span>} <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>#{$i}<span class=\"hljs-selector-tag\">-x<\/span>: 1) <span class=\"hljs-selector-tag\">and<\/span> <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>#{$i}<span class=\"hljs-selector-tag\">-y<\/span>: 1) {\n       --s-r#{$i}: <span class=\"hljs-selector-tag\">running<\/span>\n    }\n    <span class=\"hljs-keyword\">@keyframes<\/span> r#{$<span class=\"hljs-selector-tag\">i<\/span>} {1%,to {--r#{$i}<span class=\"hljs-selector-pseudo\">:1<\/span>}}\n  }\n  $<span class=\"hljs-selector-tag\">sum<\/span>: (\"<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--r1<\/span>)\");\n  <span class=\"hljs-keyword\">@for<\/span> $i from <span class=\"hljs-number\">2<\/span> to ($c+<span class=\"hljs-number\">1<\/span>) {\n    $<span class=\"hljs-selector-tag\">sum<\/span>: <span class=\"hljs-selector-tag\">append<\/span>($<span class=\"hljs-selector-tag\">sum<\/span>,'+ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--r<\/span>#{$i})' , <span class=\"hljs-selector-tag\">space<\/span>);\n  }\n  <span class=\"hljs-selector-tag\">--sum<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(#{$sum});\n  <span class=\"hljs-selector-tag\">animation<\/span>: #{$anim};\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For each coin, I will define one animation, one container query, and one @keyframe. <\/p>\n\n\n\n<p>Notice how the configuration is similar to the one we used previously. When Mario touches the coin (<code>--ci-x<\/code> and\u00a0<code>--ci-y<\/code>\u00a0are equal to\u00a0<code>1<\/code>) we run an animation that will update the variable\u00a0<code>--ri<\/code>\u00a0from\u00a0<code>0<\/code>\u00a0to\u00a0<code>1<\/code>\u00a0and will maintain its value. In other words, a variable is incremented from\u00a0<code>0<\/code>\u00a0to\u00a0<code>1<\/code>\u00a0when a coin is touched and we have as many variables as coins in the game.<\/p>\n\n\n\n<p>Then we define another variable that is the sum of all of them. That variable will contain the score of the game. To show the score we combine that variable with a counter and we rely on a pseudo-element like the below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.result<\/span><span class=\"hljs-selector-pseudo\">:before<\/span> {\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-string\">\"SCORE - \"<\/span> <span class=\"hljs-built_in\">counter<\/span>(r);\n  <span class=\"hljs-attribute\">counter-reset<\/span>: r <span class=\"hljs-built_in\">var<\/span>(--sum);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Each time Mario collects a coin, the counter is reset with a new value, and the score is updated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-final-screen\">The Final Screen<\/h2>\n\n\n\n<p>To end the game, I will also rely on the sum variable and a style query. We test if the sum is equal to the number of coins. If that\u2019s the case, we update some of the CSS to show the final screen.<\/p>\n\n\n\n<p>The code will look like the below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.result<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: r;\n}\n<span class=\"hljs-keyword\">@container<\/span> r style(<span class=\"hljs-attribute\">--sum:<\/span> #{$<span class=\"hljs-selector-tag\">c<\/span>}) {\n  <span class=\"hljs-attribute\">.result<\/span>:after {\n    <span class=\"hljs-comment\">\/* the CSS of the final screen *\/<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"608\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.10.54%E2%80%AFAM.png?resize=1024%2C608&#038;ssl=1\" alt=\"\" class=\"wp-image-2963\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.10.54%E2%80%AFAM.png?resize=1024%2C608&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.10.54%E2%80%AFAM.png?resize=300%2C178&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.10.54%E2%80%AFAM.png?resize=768%2C456&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.10.54%E2%80%AFAM.png?w=1522&amp;ssl=1 1522w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>For this style query, it\u2019s important to register the sum variable using&nbsp;<code>@property<\/code>&nbsp;so that the browser can correctly evaluate its value and compare it with the number of coins.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --sum {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">integer<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We don\u2019t need to do this with the other variables as there is no calculation to be done. They are either equal to&nbsp;<code>0<\/code>&nbsp;or&nbsp;<code>1<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What about the timer?<\/h2>\n\n\n\n<p>I deliberately skipped that part to make it your homework. The timer is closely related to the final screen and I let you dissect&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/OJYbVWP\">the original code<\/a>&nbsp;to see how it works (when it starts, when it stops, etc). You will see that it\u2019s the easiest part of the game. It\u2019s also an opportunity to inspect the other parts of the code that I skipped.<\/p>\n\n\n\n<p>We are done! Now, you know the secret behind my&nbsp;<a href=\"https:\/\/css-games.com\/super-css-mario\/\">\u201cSuper CSS Mario\u201d<\/a>&nbsp;game. With a clever combination of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_scroll-driven_animations\">scroll-driven animations<\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_containment\/Container_size_and_style_queries#container_style_queries_2\">style queries<\/a>, we can create a CSS-only game playable with keyboard navigation.<\/p>\n\n\n\n<p>Take the time to digest what you have learned so far before moving to the next sections. I will share with you two more games but I will get faster with the explanation since the techniques used are almost the same. If you are struggling with some of the concepts, give it another read.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"super-css-mario-ii\">Super CSS Mario II<\/h2>\n\n\n\n<p>Let\u2019s update the previous game and increase its difficulty by adding some enemies. In addition to collecting the coins, you need to also avoid the Goombas.&nbsp;<a href=\"https:\/\/css-games.com\/super-css-mario-2\">Play \u201cSuper CSS Mario II\u201d<\/a><\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='367' src='https:\/\/videopress.com\/embed\/5PKYQgIO?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Adding enemies to the game may sound tricky but it\u2019s pretty easy since touching them will simply stop the game. The enemies will share the same code structure as the coins. The only difference is that all of them will control one variable. If one enemy is touched, the variable is updated from&nbsp;<code>0<\/code>&nbsp;to&nbsp;<code>1<\/code>&nbsp;and the game ends.<\/p>\n\n\n\n<p>The HTML code looks like below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"game\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mario\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>     \n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"coin\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"enemy\"<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"enemy\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"result\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Like the coins, I need to keep the nested structure to have the parent-child relation.<\/p>\n\n\n\n<p>For the CSS code, I will have the following for the&nbsp;<code>.result<\/code>&nbsp;element.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.result<\/span> {\n  $<span class=\"hljs-attribute\">anim<\/span>: <span class=\"hljs-built_in\">append<\/span>($anim,<span class=\"hljs-string\">'enemy .1s forwards var(--eeee,paused)'<\/span>,comma);\n  <span class=\"hljs-attribute\">--ee<\/span>: <span class=\"hljs-number\">0<\/span>;\n  @for $i from 1 to ($e+1) {\n    @container e#{$i} <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--e<\/span>#{$i}<span class=\"hljs-selector-tag\">-x<\/span>: 1) <span class=\"hljs-selector-tag\">and<\/span> <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--e<\/span>#{$i}<span class=\"hljs-selector-tag\">-y<\/span>: 1) {\n      <span class=\"hljs-attribute\">--eeee<\/span>: running\n    }\n  }\n  <span class=\"hljs-keyword\">@keyframes<\/span> enemy {1%,<span class=\"hljs-selector-tag\">to<\/span> {<span class=\"hljs-attribute\">--ee<\/span>:<span class=\"hljs-number\">1<\/span>}}\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In addition to the previous animations defined for each coin, we add an extra animation that will control the variable&nbsp;<code>--ee<\/code>. All the style queries of the enemies will update the same animation which means if one of them is touched the variable will be equal to&nbsp;<code>1<\/code>.<\/p>\n\n\n\n<p>Then, for the final screen, we will have two conditions. Either the sum reaches the number of coins and you win or the enemy variable is equal to 1 and it\u2019s a game over!<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.result<\/span> {\n  <span class=\"hljs-attribute\">container-name<\/span>: r;\n}\n<span class=\"hljs-keyword\">@container<\/span> r style(<span class=\"hljs-attribute\">--sum:<\/span> #{$<span class=\"hljs-selector-tag\">c<\/span>}) {\n  <span class=\"hljs-attribute\">.result<\/span>:after {\n    <span class=\"hljs-comment\">\/* you win *\/<\/span>\n  }\n}\n<span class=\"hljs-keyword\">@container<\/span> r style(<span class=\"hljs-attribute\">--ee:<\/span> <span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-class\">.result<\/span><span class=\"hljs-selector-pseudo\">:after<\/span> {\n    <span class=\"hljs-comment\">\/* game over *\/<\/span>\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here is&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/JjqEdJv\">the Pen<\/a>&nbsp;to see the full Sass code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"a-css-only-maze-game\">A CSS-only Maze game<\/h2>\n\n\n\n<p>One more game? Let\u2019s go! This time it\u2019s a maze game where the character needs to grab an object without touching the wall of the maze.&nbsp;<a href=\"https:\/\/css-games.com\/maze\/\">Click to play the maze game<\/a>.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='313' src='https:\/\/videopress.com\/embed\/Y5yw8KHV?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>The cool part about this game is that we have discrete movements, unlike the previous ones. It makes the game more realistic and similar to those retro games we enjoyed playing. The wall and the Dino are similar to the enemies and the coins of the previous game so I won\u2019t detail them. I will focus on the movement and let you dissect the code of the other parts alone (here is the&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/qBGVYLM\">Pen<\/a>).<\/p>\n\n\n\n<p>Let\u2019s start with the following demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_ZENaPPG\/bdd15282dc854a3e77d6e40232ee5787\" src=\"\/\/codepen.io\/anon\/embed\/ZENaPPG\/bdd15282dc854a3e77d6e40232ee5787?height=450&amp;theme-id=47434&amp;slug-hash=ZENaPPG\/bdd15282dc854a3e77d6e40232ee5787&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed ZENaPPG\/bdd15282dc854a3e77d6e40232ee5787\" title=\"CodePen Embed ZENaPPG\/bdd15282dc854a3e77d6e40232ee5787\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Press the bottom arrow key to scroll and you will notice that the value will increment by a specific amount (it\u2019s equal to&nbsp;<code>40<\/code>&nbsp;for me). If you keep tapping a lot of times, the value will keep increasing by the same amount.<\/p>\n\n\n\n<p>This demonstrates that one click will always move the scroll by the same amount (as long as you don\u2019t keep the key pressed). This information is what I need to create the discrete movement. If the game didn\u2019t work well for you then it\u2019s probably related to that value. In&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/qBGVYLM\">the Pen<\/a>, you can update that value to match the one you get from the previous demo.<\/p>\n\n\n\n<p>Now let\u2019s suppose we want a maze with 10 columns and 5 rows. It means that we need 9 clicks to reach the last column and 4 clicks to reach the last row. The horizontal overflow needs to be equal to&nbsp;<code>360px=(40px*9)<\/code>&nbsp;while the vertical overflow needs to be equal to&nbsp;<code>160px=(40px*4)<\/code>.<\/p>\n\n\n\n<p>Let\u2019s turn this into a code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.container<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">500px<\/span>;  <span class=\"hljs-comment\">\/* 50px * 10 *\/<\/span>\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">250px<\/span>; <span class=\"hljs-comment\">\/* 50px * 5  *\/<\/span>\n}\n<span class=\"hljs-selector-class\">.container<\/span> <span class=\"hljs-selector-tag\">div<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>:  <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> + <span class=\"hljs-number\">40px<\/span>*<span class=\"hljs-number\">9<\/span>);\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> + <span class=\"hljs-number\">40px<\/span>*<span class=\"hljs-number\">4<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The&nbsp;<code>50px<\/code>&nbsp;I am using is an arbitrary value that will control the size of the grid.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_VwOovOg\/638814272ce47dd6860c3432f946daa5\" src=\"\/\/codepen.io\/anon\/embed\/VwOovOg\/638814272ce47dd6860c3432f946daa5?height=450&amp;theme-id=47434&amp;slug-hash=VwOovOg\/638814272ce47dd6860c3432f946daa5&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed VwOovOg\/638814272ce47dd6860c3432f946daa5\" title=\"CodePen Embed VwOovOg\/638814272ce47dd6860c3432f946daa5\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Try to scroll the container using the keyboard and you will notice that you need exactly 9 clicks horizontally and 4 clicks vertically to scroll the whole content.<\/p>\n\n\n\n<p>Then we can follow the same logic as the Mario game (the sticky container, the character, etc) but with a small difference: the&nbsp;<code>x<\/code>&nbsp;and&nbsp;<code>y<\/code>&nbsp;animations will animate integer variables instead of the&nbsp;<code>top<\/code>&nbsp;and&nbsp;<code>left<\/code>&nbsp;properties.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --x {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">integer<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0\n}\n<span class=\"hljs-keyword\">@property<\/span> --y {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: '&lt;<span class=\"hljs-selector-tag\">integer<\/span>&gt;';\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0\n}\n<span class=\"hljs-selector-class\">.character<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">50px<\/span>; <span class=\"hljs-comment\">\/* the same value used to control the size of the grid *\/<\/span>\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--x)*<span class=\"hljs-number\">100%<\/span>) <span class=\"hljs-built_in\">calc<\/span>(var(--y)*<span class=\"hljs-number\">100%<\/span>);\n  <span class=\"hljs-attribute\">animation<\/span>: x linear,y linear;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">scroll<\/span>(nearest inline),<span class=\"hljs-built_in\">scroll<\/span>(nearest block);\n}\n<span class=\"hljs-keyword\">@keyframes<\/span> x { <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-number\">9<\/span> } }\n<span class=\"hljs-keyword\">@keyframes<\/span> y { <span class=\"hljs-selector-tag\">to<\/span> { <span class=\"hljs-attribute\">--y<\/span>: <span class=\"hljs-number\">4<\/span> } }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_GRaVoRK\/6f401433b8e48dc3c50073b4bb163281\" src=\"\/\/codepen.io\/anon\/embed\/GRaVoRK\/6f401433b8e48dc3c50073b4bb163281?height=450&amp;theme-id=47434&amp;slug-hash=GRaVoRK\/6f401433b8e48dc3c50073b4bb163281&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed GRaVoRK\/6f401433b8e48dc3c50073b4bb163281\" title=\"CodePen Embed GRaVoRK\/6f401433b8e48dc3c50073b4bb163281\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>We have a discrete keyboard movement using only CSS! Not only that, but thanks to the variable&nbsp;<code>--x<\/code>&nbsp;and&nbsp;<code>--y<\/code>&nbsp;we can know where our character is located within the grid.<\/p>\n\n\n\n<p>You know the rest of the story, we apply style queries on those variables to know if the character hit a wall or if it reaches the Dino! I let you dissect&nbsp;<a href=\"https:\/\/codepen.io\/t_afif\/pen\/qBGVYLM\">the code<\/a>&nbsp;as a small exercise and why not update it to create your own maze version? It could be a fun exercise to practice what we have learned together. Fork it and share your own maze version in the comment section.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>I hope you enjoyed this CSS experimentation. It\u2019s OK if you were a bit lost at times and didn\u2019t fully understand all the tricks. What you need to remember is that scroll-driven animations allow us to link the scrolling progress to any kind of animation and style queries allow us to conditionally apply any kind of CSS based on the value of custom properties (CSS variables). Everything else depends on your creativity. I was able to create \u201cSuper CSS Mario\u201d and a maze game but I am pretty sure you could do even better.<\/p>\n\n\n\n<p>One day, someone will create a fully playable FPS using only CSS. Keyboard to move the character and mouse to kill enemies. Why not, nothing is impossible using CSS!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The navigation in this game works with the arrow keys, which is made possible with scroll driven animations, faked collision detection, and maintaining state with CSS custom properties.<\/p>\n","protected":false},"author":12,"featured_media":2974,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,199,57],"class_list":["post-2936","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-game","tag-scroll-driven-animations"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-07-at-10.27.08%E2%80%AFAM.png?fit=1450%2C850&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2936","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=2936"}],"version-history":[{"count":17,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2936\/revisions"}],"predecessor-version":[{"id":2980,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/2936\/revisions\/2980"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/2974"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=2936"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=2936"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=2936"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}