<input type="radio" name="axis" id="horizontal">
<input type="radio" name="axis" id="vertical" checked>
<input type="radio" name="axis" id="applicate">

<div class="wrapper">
  <input type="checkbox" name="play" id="play">

  <label for="play" class="playBtn"></label>
  <div class="scene">
    <div class="wall"></div>      
  </div>
</div>

<nav class="cartesian">

  <p>MO&#11167;E<br><small>along:</small></p>

  <div class="labels">
    <label class="axes xAxis" for="horizontal">
      <span>&#x2B0D;</span><span>X</span>
    </label>
    <label class="axes yAxis" for="vertical">
      <span>&#x2B0D;</span><span>Y</span>
    </label>
    <label class="axes zAxis" for="applicate">
      <span>&#x2B0D;</span><span>Z</span>
    </label>
  </div>

</nav>
body {
  margin: 0; /* this overrides the default margin - which is crucial if your background isn't white/blank/undefined */
  box-sizing: border-box; /* another one of those standard overrides that you'll need to put in every darn time */
  background-color: black; /* the hex code for dark gray, the equivalent of rgb(48,48,48) or hsl(0deg 0% calc( 100% / 16 *3)), and a shorthard for #333333 )*/
  display: flex; /* you could alternately use grid */
  flex-direction: row; /* items are arranged horizontally - i.e. sitting next to each other */
  justify-content: center; /* groups the objects together at the center horizontally */
  align-items: center; /* centers every object vertically */
  min-height: 100vh; /* we need the viewport (the browser window) to be 100vh (100 viewport height units) so that there is enough space to suspend the object in */
  font-size: .25vw; /* sets a fontsize dependent on the height of the viewport */
  overflow: hidden;
}

.wrapper {
  display: grid; /* to center the PLAY button */
}

.scene {
  height: 100em; /* sets the width to be 100 units of the font size, thus ensuring responsive scaling that mimics percentages */
  aspect-ratio: 1; /* makes sure the height matches the width of the container, making it a square*/
  perspective: 250em; /* sets the distance of the perspective */
  perspective-origin: 75% 25%;
  position: relative; /* makes the position of the container independent of any other neighboring container - placed wherever you want rather than relative to its neighbors, and free to overlap */
  /* scale: 1.2; */
}

.scene * {
  transform-style: preserve-3d;
  position: absolute; /*see absove*/
  inset: 0; /* gives the containers nested in the .scene tag a margin of 0, thus making them fill it 100% in width and height; this is shorthand for top, right, bottom, left */
}

.wall {
  border: 3em solid white; /* each wall has a white border - which is contained within the wall's width (this is what box-sizing: border-box does) */
  box-shadow: 0 0 3em 2px lime, inset 0 0  3em 2px lime; /* adds a green glow to the white lines */
  animation: move 2.5s ease-in-out alternate infinite;
}

.wall::before, .wall::after {
  content: '';
  position: absolute;
  inset: -30em;
  --arrowShaft: transparent 46%, black 46% 54%, transparent 54%;
  --arrowHead: black 10%, transparent 10% 90%, black 90%;
}

.wall::before {
  background: radial-gradient(#0004,#0000), conic-gradient(from 45deg, transparent 90deg, #f40 90deg 130deg, #f80 140deg 180deg, transparent 180deg 270deg, #f80 270deg 310deg, #f40 320deg), linear-gradient(90deg,#bf0,#080);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); /* cuts out a square rotated by 45deg by connection the midpoints of each side */
  -webkit-mask-image:
    linear-gradient(var(--arrowShaft)), /* horizontal arrow's shaft */
    linear-gradient(90deg, var(--arrowHead)), /* horizontal arrow's head */
    linear-gradient(90deg, var(--arrowShaft)), /* vertical arrow's shaft */
    linear-gradient(var(--arrowHead)); /* vertical arrow's head */
}

.wall::after {
  background: radial-gradient(#00f, #0ff 70.71%);
  clip-path: polygon(0 50%, 25% 25%, 75% 75%, 100% 50%, 75% 25%, 25% 75%); /* cuts out a square rotated by 45deg by connection the midpoints of each side */
  -webkit-mask-image: linear-gradient(var(--arrowShaft)), linear-gradient(90deg, var(--arrowHead));
}

@keyframes move { /* the cube makes a full 360 degree turn */
  100% {
    transform: var(--move);
  }
}


/* OPTIONAL READING

This section details the appearance and the functioning of the play/pause toggler. It is not a part of the cube - but still a fun thing to learn if you're up for it.

*/

input {
  display: none; /* hides the input, we'll have a label for it instead that is nicer than a tick-box */
}

.playBtn { /* that's the label */
  position: absolute;
  width: 40em;
  aspect-ratio: 1;
  z-index: 1; /* places the label on a layer above the cube - as otherwise it'd be inside the 3D solid, and you wouldn't be able to interact with it */
  display: grid; /* this label will have nested content, and we want it centered */
  place-self: center; /* since its parent's display: grid, this object can center itself; very useful! - and flex can't do that */
  place-items: center; /* the object's own display is grid - because it doesn't automatically inherit display from its parent, and we want to center its children, too. */
  transition: all .5s ease-in-out; /* when you interact with the button - hover, check/uncheck the input - it will change appearance. This makes it switch between each look smoothly rather than instantly */
}

.playBtn::before, .playBtn::after { /* these are a kind of containers within a container; they aren't declared in the HTML by you - but will show up in the page inspector */
  content: ''; /* text or special characters go here - but we don't need any, we just want containers */
  position: absolute;
  inset: 0 0 50% 0; /* this creates a container that covers the upper half of the space available*/
  border-style: solid;
  border-color: #fff #fff0 #fff0 #fff; /* the top and the left border are white, the right and the bottom are transparent (white but with opacity of 0) */
  border-width: 3em 0 0 3em; /* the top and the left border are 3em wide, the right and the bottom are 0px */
  transform-origin: 0% calc(50% - 50%*var(--dir)); /* this places the pivot of the container in its bottom left corner */
  transform: skewY(25deg); /* creates a downward rhomboid */
  transition: inherit; /* borrows transition properties from its parent container */
  filter: drop-shadow(0 0 3em lime);
}

.playBtn::before {
  --dir: 1; /* a custom variable for the direction of transforms; translations applied to the container will be multiplied by this variable; here it changes nothing... */
}

.playBtn::after {
  --dir: -1; /* ...but here it makes the translation take on a negative value, and thus go in the other direction */
  scale: 1 -1; /* the scale on the horizontal axis (the first number) stays the same, but on the vertical (the latter) it is negative, so the object is flipped upside down*/
}

.playBtn:hover { /* when you mouse over the label... */
  scale: 1.25; /* ...it gets a little bit larger */
}

.playBtn:hover::before,
.playBtn:hover::after  {
  filter: drop-shadow(0 0 3em lime); /* ...and it starts glowing brighter */
}

#play:not(:checked) ~ .scene .wall { /* if the PLAY button hasn't been pressed */
  animation-play-state: paused; /* ...the animation is paued */
}

#play:checked ~ .playBtn { /* when you hit the PLAY button */
  rotate: -90deg; /* ...it turns to its side */
  translate: -190% -130%;
}

#play:checked ~ .playBtn::before,
#play:checked ~ .playBtn::after  {
  transform: none;
  inset: 35% 0;
  border-color: #fff;
  border-width: 3em;
  translate: 0 calc(-80%*var(--dir)); /* ...and turns into a pause button */
  transform-origin: 50% 50%;
}

.cartesian { /* the navbar for perspective mode GUI */
  margin-top: 0;
  margin-left: 40em;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.labels {
  display: inherit;
  flex-direction: inherit;
}

p {
  color: #fff;
  text-shadow: 0 0 3em lime;
  font-size: 18em;
  font-family: Verdana;
  margin: 0.25em;
}

small {
  font-size: .5em;
}

.axes {
  position: relative;
  width: 30em;
  aspect-ratio: 1;
  transition: all .3s ease-in-out;
  display: flex;
  align-items: center;
}

.axes:nth-child(1) {
  --hueOffset: 0;
}

.axes:nth-child(2) {
  --hueOffset: 1;
}

.axes:nth-child(3) {
  --hueOffset: 2.25;
  perspective: 20em;
  perspective-origin: 50% 50%;
}

.axes span {
  font-size: 30em;
  font-family: Helvetica;
  color: hsl(calc(120deg*(1 - var(--hueOffset))) 100% 25%);
}

.axes span:nth-child(2) {
  transition: all .6s ease-out;
}

.xAxis span:nth-child(1) {
  rotate: 90deg;
}

.zAxis span:nth-child(1) {
  transform: rotate(90deg) rotateX(85deg);
  pointer-events: none;
}

.xAxis span:nth-child(2) {
  text-shadow: 0 0 #080;
}

.yAxis span:nth-child(2) {
  text-shadow: 0 0 #800;
}

p, .axes span {
  font-weight: bolder;
  text-align: center;
}

#horizontal:checked ~ .cartesian .xAxis,
#vertical:checked ~ .cartesian .yAxis,
#applicate:checked ~ .cartesian .zAxis {
  -webkit-text-stroke: 0 var(--clr);
  text-shadow: 0 0 0 var(--clr);
  color: var(--clr);
}

:is(#horizontal:checked ~ .cartesian .xAxis, #vertical:checked ~ .cartesian .yAxis, #applicate:checked ~ .cartesian .zAxis) span {
  color: hsl(calc(120deg*(1 - var(--hueOffset))) 100% 50%);
}

#horizontal:checked ~ .cartesian .xAxis span:nth-child(2) {
  translate: -.05em 0 0;
  text-shadow: .1em 0 0 #080;
}

#vertical:checked ~ .cartesian .yAxis span:nth-child(2) {
  translate: 0 -.05em 0;
  text-shadow: 0 .1em 0 #800;
}

#applicate:checked ~ .cartesian .zAxis span:nth-child(2) {
  scale: 1.25;
}

#horizontal:checked ~ .wrapper .wall {
  --move: translateX(25em);
}

#vertical:checked ~ .wrapper .wall {
  --move: translateY(25em);
}

#applicate:checked ~ .wrapper .wall {
  --move: translateZ(50em);
}

#horizontal:checked ~ .wrapper .wall:after {
  transform: rotateX(90deg) rotate(90deg);
}

#vertical:checked ~ .wrapper .wall:after {
  rotate: y 90deg;
}

#applicate:checked ~ .wrapper .wall:after {
  clip-path: circle(4%);
  background-image: repeating-conic-gradient(#00f,#08f,#00f 50%);
}

@media (orientation: portrait) { /* for mobile screens displaying in portait mode (a tall, narrow rectangle) */
  body {
    font-size: .25vh; /* the font size is now depended on the viewport width - rather than height - since now it's the narrower of the two dimensions */
    flex-direction: column; /* in mobile/portrait mode the scene and the navigation need to be stacked vertically rather than horizontally */
  }

  p {
    font-size: 24em;
  }

  .cartesian {
    margin-left: 0;
    margin-top: 50em; /* a little margin separating the navbar from the cube - since it's a little too large in the single-point perspective mode*/
    aspect-ratio: 3;
  }

}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.