<form class="game">
<div class="game__controls">
<input class="sr-only" type="checkbox" id="start" />
<input class="sr-only" type="checkbox" id="finish" />
</div>
<div class="game__stage">
<svg
viewBox="0 0 1920 1080"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<!-- <path fill="#fff" d="M0 0h1920v1080H0z" /> -->
<g class="game__platform platform">
<path class="platform__side platform__side--front" fill="#D9D9D9" d="M0 958h1628v122H0z" />
<path class="platform__side platform__side--right" d="m1628 958 292-122v122l-292 122V958Z" fill="#AAA" />
<path
class="platform__side platform__side--top"
d="M291.544 836H1919.5L1628 958H0l291.544-122Z"
fill="#F3F3F3"
/>
<path class="platform__shadow" fill="#D9D9D9" d="M295 887h1330v20H295z" />
</g>
<g class="game__zones">
<path class="game__over" opacity=".5" fill="#EC8080" d="M0 0h1920v1080H0z" />
<path class="game__start" opacity=".5" fill="#D9D9D9" d="M-1 772h600v308H-1z"/>
</g>
<g class="game__wire">
<!-- <path
d="M299.05 897.065V199.998S631 68.998 667 144.997C719.582 256 421 215.564 421 425.001 421 648 531.138 726 667 726h132c86.921 0 142.16-81.164 146-168 4.662-105.419-200.186-80.561-196-186 3.703-93.278 75.308-149.877 166-172.002 40.216-9.81 106 0 106 0L1177 726s103.94 25.132 162 0c74.41-32.211 119.21-86.965 122-168 3.99-116.056-238.59-70.063-232-186 5.42-95.393 84.85-137.628 174-172.002 80.16-30.908 220 .001 220 .001v697.066"
stroke="#F90505"
stroke-width="80"
/> -->
<path
class="wire__segment wire__segment--main"
d="M299.05 897.065V199.998S631 68.998 667 144.997C719.582 256 421 215.564 421 425.001 421 648 531.138 726 667 726h132c86.921 0 142.16-81.164 146-168 4.662-105.419-200.186-80.561-196-186 3.703-93.278 75.308-149.877 166-172.002 40.216-9.81 106 0 106 0L1177 726s103.94 25.132 162 0c74.41-32.211 119.21-86.965 122-168 3.99-116.056-238.59-70.063-232-186 5.42-95.393 84.85-137.628 174-172.002 80.16-30.908 220 .001 220 .001v697.066"
stroke="#000"
stroke-width="20"
/>
<path
class="wire__segment wire__segment--top"
d="M1623 199.999s-139.84-30.909-220-.001c-57.28 22.085-110.54 47.414-142.74 88.002"
stroke="#C805F9"
stroke-width="80"
/>
<path
class="wire__segment wire__segment--right"
d="M1259.17 431.088c-19.52-13.562-31.72-31.859-30.17-59.088 1.94-34.105 13.34-61.415 31.26-84"
stroke="#F96B05"
stroke-width="80"
/>
<path
class="wire__segment wire__segment--bottom"
d="M1427.09 497.289c-45.25-29.326-124.61-36.105-167.92-66.201"
stroke="#05F9BE"
stroke-width="80"
/>
<path
class="wire__segment wire__segment--left"
d="M1427.09 660c20.71-27.786 32.52-61.632 33.91-102 .97-28.314-12.73-46.983-33.91-60.711"
stroke="#F905B4"
stroke-width="80"
/>
<path
class="wire__segment wire__segment--top"
d="M1177 726s103.94 25.132 162 0c37.34-16.165 67.23-38.007 88.09-66"
stroke="#05A1F9"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--top"
d="M1021 199.998s-65.784-9.81-106 0C855.803 214.44 804.737 243.569 775.38 288"
stroke="#F9B405"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--left"
d="M779 431.088c-18.818-14.196-31.055-32.499-30-59.088 1.286-32.393 10.761-60.362 26.38-84"
stroke="#05F91E"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--bottom"
d="M927.624 510C894.142 473.747 820.538 462.424 779 431.088"
stroke="#F9055D"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--left"
d="M887 691.724c34.953-31.719 55.699-81.703 58-133.724.891-20.155-5.876-35.548-17.376-48"
stroke="#F99705"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--top"
d="M555 703.62C588.593 719.124 626.663 726 667 726h132c34.85 0 64.607-13.047 88-34.276"
stroke="#0575F9"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--right"
d="M447.461 332C431.185 356.162 421 386.32 421 425.001c0 156.791 54.447 241.901 134 278.619"
stroke="#C8F905"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--top"
d="M299.05 199.998S557.432 98.032 643 126.21"
stroke="#F90531"
stroke-width="80"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
class="wire__segment wire__segment--bottom"
d="M446.136 334c52.059-79.996 170.293-95.303 212.093-134.001"
stroke="#F9A605"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--right"
d="M647 127.674c9.374 3.806 16.273 9.455 20 17.323 11.419 24.106 6.277 41.07-8.771 55.001"
stroke="#05F9F9"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--right"
d="M1623 897.065V199.999"
stroke="#61FF00"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--right"
d="m1177 726-156-526.002"
stroke="#4405F9"
stroke-width="80"
stroke-linecap="round"
/>
<path
class="wire__segment wire__segment--left"
d="M299.05 897.065V199.999"
stroke="#D705F9"
stroke-width="80"
stroke-linecap="round"
/>
</g>
<g class="game__zones">
<circle class="game__success" cx="1623" cy="906" r="125" fill="#D9D9D9" />
</g>
</g>
</svg>
</div>
<!-- The trick is to have your screens inside the <form> and the screens are powered by the <form> -->
<!-- For an intro have a separate dismissal [type="checkbox"] outside of the <form>.
that doesn't get reset and hides that one [BELOW]-->
<div class="screen screen--intro">
<!-- Intro -->
<div class="screen__dialog dialog dialog--intro">
<h2>steady :has()</h2>
<p>How steady are those hands? Let's find out.</p>
<p>Hit "Start" and follow the wire to the end.</p>
<div class="game__control-container">
<label class="faux-button" for="start">Start</label>
</div>
</div>
</div>
<div class="screen screen--win">
<!-- Win -->
<div class="screen__dialog dialog dialog--win">
<h2>Winner!</h2>
<p>You're not a robot. Or are you?</p>
<div class="game__control-container">
<button type="reset" class="faux-button">Replay</label>
</div>
</div>
</div>
<div class="screen screen--lose">
<!-- Lose -->
<div class="screen__dialog dialog dialog--lose">
<h2>Uh Oh</h2>
<p>Try again perhaps?</p>
<div class="game__control-container">
<button type="reset" class="faux-button">Replay</label>
</div>
</div>
</div>
</form>
<div class="timeout"></div>
:root {
--bg: var(--blue-1);
--size: 100vmin;
--red: #ff4d00;
--yellow: #ebb624;
--green: #38b24d;
--blue: #0f73ff;
--delay-step: 0.5s;
--stage-speed: 0.5s;
--wire-speed: 1.5s;
--backdrop-speed: 0.65s;
--dialog-speed: 0.75s;
--timer-speed: 20s;
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
display: grid;
place-items: center;
min-height: 100vh;
background: var(--bg);
overflow: hidden;
font-family: 'Google Sans', sans-serif, system-ui;
}
svg {
cursor: url(https://assets.codepen.io/605876/handle--north.png) 64 32, pointer;
}
.wire__segment--top {
cursor: url(https://assets.codepen.io/605876/handle--south.png) 64 96, pointer;
}
.wire__segment--right {
cursor: url(https://assets.codepen.io/605876/handle--west.png) 32 64, pointer;
}
.wire__segment--bottom {
cursor: url(https://assets.codepen.io/605876/handle--north.png) 64 32, pointer;
}
.wire__segment--left {
cursor: url(https://assets.codepen.io/605876/handle--east.png) 96 64, pointer;
}
.game {
width: var(--size);
}
svg {
width: 100%;
height: auto;
}
.screen {
position: fixed;
inset: 0;
height: 100vh;
width: 100vw;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(var(--display-screen, 0));
background: hsl(0 0% 0% / 0.75);
z-index: 2;
}
.screen__dialog {
width: var(--size);
aspect-ratio: 16 / 9;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateY(calc((1 - var(--show, 0)) * 100vh));
transition: transform 0.25s var(--ease-elastic-2);
font-size: clamp(1rem, 2vmin, 2rem);
backdrop-filter: blur(10px);
color: var(--gray-0);
background: hsl(210 29% 20% / 0.9);
box-shadow: var(--shadow-6);
text-align: center;
border-style: solid;
border-top-color: var(--red);
border-right-color: var(--green);
border-bottom-color: var(--yellow);
border-left-color: var(--blue);
border-width: var(--size-2);
display: grid;
place-items: center;
align-content: center;
}
.screen__dialog h2 {
color: var(--gray-0);
}
/* Aesthetics */
.wire__segment {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.wire__segment:not(.wire__segment--main), .game__zones > * {
opacity: 0;
}
.wire__segment--main {
stroke-dasharray: 5408;
stroke: var(--red-3);
}
/* Animations Timeline with Custom Properties */
.screen--intro {
animation: enter var(--backdrop-speed) calc(var(--stage-speed) + var(--wire-speed) + (3 * var(--delay-step))) both var(--ease-in-2);
}
.screen--intro .dialog--intro {
animation: fly var(--dialog-speed) calc(var(--stage-speed) + var(--wire-speed) + var(--backdrop-speed) + (4 * var(--delay-step))) both var(--ease-elastic-2);
}
@keyframes fly {
from {
transform: translate(-50%, -50%) translateY(100vh);
}
}
@keyframes enter {
from {
opacity: 0;
}
}
.wire__segment--main {
animation: draw var(--wire-speed) calc(var(--stage-speed) + (2 * var(--delay-step))) both var(--ease-squish-2)
}
@keyframes draw {
from {
stroke-dashoffset: 5408;
}
}
.game__stage {
animation: bump var(--stage-speed) var(--delay-step) both var(--ease-elastic-2);
}
@keyframes bump {
0% {
transform: translateY(100vh);
}
}
/* Utility */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.faux-button {
--bg: var(--gray-9);
box-sizing: border-box;
font-size: var(--font-size-fluid-0);
padding: var(--size-4) var(--size-6);
background: var(--bg);
border-radius: var(--size-4);
color: var(--gray-0);
font-weight: bold;
font-family: 'Google Sans', sans-serif;
border: var(--size-2) solid var(--gray-0);
display: inline-block;
transform: translateY(calc(var(--y, 0) * 1%)) scale(var(--s));
transition: transform 0.1s, background 0.1s;
}
.faux-button:hover {
--bg: #000;
--y: -5;
--s: 1.1;
}
.faux-button:active {
--bg: #000;
--y: 5;
--s: 0.9;
}
/* Game specific designs */
.game__control-container {
position: absolute;
left: calc(var(--size-2) * -1);
bottom: calc(var(--size-2) * -1);
width: 31.25%;
height: 28.5%;
display: grid;
place-items: center;
}
.faux-button {
max-height: 100%;
padding: var(--size-fluid-1);
}
/* Game Mechanics */
.dialog--intro {
--show: 1;
}
:root:has(#start:checked) :is(.dialog--intro) {
--show: 0;
}
.screen--intro {
--display-screen: var(--display-intro, 1);
}
.screen--lose {
--display-screen: var(--display-lose, 0);
}
.screen--win {
--display-screen: var(--display-win, 0);
}
:root:not(:has(#start:checked)) {
--display-intro: 1;
}
:root:has(#start:checked) {
--display-intro: 0;
}
:root:has(#start:checked) .timeout {
animation: time-out 0.5s var(--timer-speed) both steps(1);
}
:root:has(#start:checked) body:after {
animation: countdown var(--timer-speed) ease-out;
}
body:after {
content: "";
height: var(--size-2);
width: 100vw;
transform-origin: 0 50%;
background: var(--green-6);
position: fixed;
top: 0;
clip-path: inset(0 100% 0 0);
}
@keyframes countdown {
0% {
background: var(--green-6);
clip-path: inset(0 0 0 0);
}
to {
background: var(--red-6);
clip-path: inset(0 100% 0 0);
}
}
.timeout {
position: fixed;
inset: 0;
transform: scale(0);
}
@keyframes time-out {
to {
transform: scale(1);
}
}
:root:has(#start:checked):has(.game__over:hover, .screen--lose:hover, .timeout:hover) .dialog--lose {
--show: 1;
}
:root:has(#start:checked):has(.game__over:hover, .screen--lose:hover, .timeout:hover) .screen--lose {
--display-lose: 1;
}
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover) .dialog--win {
--show: 1;
}
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover) .screen--win {
--display-win: 1;
}
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover) .timeout,
:root:has(#start:checked):has(.game__over:hover, .screen--lose:hover) .timeout,
:root:has(#start:checked):has(.game__success:hover, .screen--win:hover) body:after,
:root:has(#start:checked):has(.game__over:hover, .screen--lose:hover) body:after {
animation: none;
}