link(href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap", rel="stylesheet")

// secret codes
- var codes = ["6543", "1324", "4135", "5614"];
- var rounds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- var numrounds = 10;
- var slots = [1, 2, 3, 4];

form
  label#info(for="hideInit") i
  input(id="color-blind", type="checkbox")
  label#color-blind-label(for="color-blind") Add letters
  // rounds
  div#board
    h1#mastermind MASTERMIND
    each val in codes
      input(type="radio", tabindex=-1, value=val, name="code", id="code-"+val, checked=(val === "6543"))

    each round in rounds
      input(type="checkbox", tabindex=-1, id="completed-"+round, class="completed")
      input(type="radio", tabindex=-1, id="round-"+round, name="round")
      each val in slots
        each color in [1,2,3,4,5,6]
          input(type="radio", tabindex=-1, value=color, name="slot-"+val+"-round-"+round, id="slot-"+val+"-round-"+round+"-color-"+color)
      div(class="round-container round-"+round)
        label(for="completed-"+round) 
          span NEXT
        each num in [1,2,3,4]
          a(href="#round-"+round+"-slot-"+num, tabindex=0, class="slot-circle slot-"+num+"-round-"+round+"-trigger")
        div(class="valid valid-"+round)
          each num in [1,2,3,4]
            div(class="correct-"+num)
          each num in [1,2,3,4]
            div(class="partial-"+num)
          each num in [1,2,3,4]
            div(class="empty-"+num)

    input(type="radio", value=val, name="round", id="round-"+(numrounds+1))
    
    input(type="checkbox", id="hideInit")
    #init.popup
      div
        h1 Let's play CSS Mastermind!
        p 
          |  
          a(href="https://en.wikipedia.org/wiki/Mastermind_(board_game)",target="_blank") Mastermind 
          | is a challenging code-breaking game. There are 6 colors and a 4-length code to decipher. Can you break the code? 
        p Select the colors for each code peg by clicking in an active slot. When you are ready, click on the "Next" button on the left side of the active round.
        p The results will be displayed using key pegs on the right. A black key peg indicates that a color is in the right place, while a white key peg indicates that a color is present but not in the right place.
        p.games Pick a game to play:
        p.games
          each val in codes
            label(class="close", for="code-"+val, class="game-selector") Game
        div#start-button
          label(for="round-1", class="green-button") START GAME
        label.close(for="hideInit") Close
          
    div.lost.popup 
      div 
        h2 Game Over!
        p Oh, no! You reach the end of the game before cracking the code... Click on the button to try again!
        div
          input(type="reset", value="Play again", class="green-button")
    div.won.popup
      div 
        h2 Congratulations! You won!
        p Yay! You are amazing and cracked the code successfully! Click on the button below to restart the game.
        div
          input(type="reset", value="Play again", class="green-button")
          
    div#confetti
      - n = 1;
      while n < 100
        div(class="confetti confetti-"+n)
        - n++

    each round in rounds
      div(id="transition-"+round, class="popup transition")
        div
          h2 Not there yet!
          p Review the hints on the right side and make some changes in the next round.
          div
            label(for="round-"+(round+1), class="close") Next round!
      each num in [1,2,3,4]
        div(id="round-"+round+"-slot-"+num, class="popup") 
          div 
            h2 Round #{round} Slot #{num}
            div.color-labels
              each color in [1,2,3,4,5,6]
                label(for="slot-"+num+"-round-"+round+"-color-"+color,tabindex=1)
            a(href="#", class="close") Close




View Compiled
$fullcodes: '6543', '1324', '4135', '5614';
$codes: '6' '5' '4' '3', '1' '3' '2' '4', '4' '1' '3' '5', '5' '6' '1' '4';
$rounds: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
$num-rounds: 10;
$slots: 1, 2, 3, 4;
$color-number: 1, 2, 3, 4, 5, 6;
$colors: '#7dd', '#096', '#f40', '#ee0', '#eee', '#d49';
$color-letters: 'B', 'G', 'O', 'Y', 'W', 'P';
$board-primary: #de8f65;
$board-secondary: #5ccfa0;

* {
  font-family: Roboto, Arial, sans-serif;
}

input:not([type=reset]) {
  position: absolute;
  left: -10000em;
}

html {
  display: table-row;
  margin: 0;
  padding: 0;
  width: 100vw;
  height: 100vh;
}

body {
  margin: 0;
  padding: 0;
  width: 100vw;
  height: 100vh;
  display: table-cell;
  text-align: center;
  vertical-align: middle;
}

h1 {
  margin: 0;
  padding: 0;
  margin-bottom: 10px;
  font-size: 24px;
}

p {
  font-weight: 300;
}

.won, 
.lost,
.won:is(:visible) ~ .popup { 
  display: none; 
}

#round-#{$num-rounds + 1}:checked ~ .lost {
  display: flex;
}

#board {
  position: relative;
  width: 380px;
  overflow: visible;
  margin: 35px auto;
  display: block;
  padding: 80px 0;
  background: #{$board-primary};
  border-radius: 15px;
  box-shadow: inset 1px 1px 3px #{$board-primary}, inset -4px 2px 8px -2px rgba(0,0,0,0.15), inset 4px 4px 8px rgba(255,255,255,0.35), inset -4px -4px 8px -2px rgba(0,0,0,0.15), inset 4px 0 8px -2px rgba(255,255,255,0.35);

  &::before {
    content: "";
    display: block;
    position: absolute;
    bottom: 0px;
    left: 50px;
    width: 180px;
    height: 60px;
    background: #{$board-secondary};
    box-sizing: border-box;
    border: 1px solid rgba(0,0,0, 0.2);
    border-radius: 3px 3px 1px 1px;
    box-shadow: inset 0 -4px 6px rgba(0,0,0,0.15), inset 1px 1px rgba(255,255,255,0.3), inset -1px 1px rgba(255,255,255,0.3);
  }
}

#color-blind-label {
  position: fixed;
  top: 10px;
  right: 10px;

  &::before {
    content: "";
    display: inline-block;
    width: 1em;
    height: 1em;
    border-radius: 50%;
    border: 2px solid #369;
    vertical-align: bottom;
    margin-right: 0.25em;
    box-shadow: inset 0 0 0 9in #fff;
  }
}

#color-blind:checked + label::before {
  box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 9in #369;
}

#mastermind {
  margin: 0;
  padding: 0;
  width: 520px;
  height:100px;
  position: absolute;
  right: 0;
  top: -20px;
  transform: rotate(-90deg);
  transform-origin: bottom right;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 70px;
  font-weight: bold;
  letter-spacing: 2px;
  color: lighten($board-secondary, 2.5%);
  text-shadow: 1px -1px 1px rgba(255,255,255,0.4), -1px 1px 1px rgba(0,0,0,0.3);
  box-sizing: border-box;
  background: linear-gradient(45deg, #{$board-secondary}, darken($board-secondary, 5%));
  border: 1px solid rgba(0,0,0, 0.2);
  border-bottom: 0;
  border-radius: 3px 3px 1px 1px;
  box-shadow: inset 0 -4px 6px rgba(0,0,0,0.15), inset 1px 1px rgba(255,255,255,0.3), inset -1px 1px rgba(255,255,255,0.3);

  &::before, &::after {
    content: "";
    display: block;
    position: absolute;
    top: 77px;
    right: -5px;
    width: 5px;
    height: 15px;
    background: rgba(0,0,0,0.35);
    border-radius: 0 2px 2px 0;
    box-shadow: inset -1px 0 2px rgba(0,0,0,0.25);
  }

  &::after {
    right: auto;
    left: -5px;
    border-radius:  2px 0 0 2px;
    box-shadow: inset -1px 0 2px rgba(0,0,0,0.25);
  }
}

// confetti effect based on Fionna's https://codepen.io/fionnachan/pen/EvaqOB
#confetti {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: none;
  pointer-events: none;
  z-index: 3;

  .confetti {
    position: absolute;
  }
}

@for $i from 1 through 99 {
  $w: random(12);
  $l: random(100);
  .confetti-#{$i} {
    width: #{$w}px;
    height: #{$w*0.6}px;
    background-color: #{nth($colors, random(6))};
    top: -10%;
    left: unquote($l+"%");
    opacity: random() + 0.5;
    transform: rotate(#{random()*360}deg);
    animation: drop-#{$i} unquote(4+random()+"s") unquote(random()+"s") infinite;
    animation-delay: -#{random()*10}s;
  }

  @keyframes drop-#{$i} {
    100% {
      top: 110%;
      left: unquote($l+random(15)+"%");
    }
  }
}

.round-container {
  border-bottom: 1px solid darken($board-primary, 10%);
  border-top: 1px solid lighten($board-primary, 10%);
  padding: 5px 10px;
  position: relative;
  width: 260px;
  height: 40px;

  &.round-1 {
    border-top: 1px solid darken($board-primary, 10%);
    box-shadow: inset 0 1px lighten($board-primary, 10%);
  }

  &.round-10 {
    border-bottom: 1px solid lighten($board-primary, 10%);
    box-shadow: inset 0 -1px darken($board-primary, 10%);
  }

  & label {
    width: 40px;
    height: 40px;
    line-height: 40px;
    text-align: center;
    display: inline-block;
    visibility: hidden;
    pointer-events: none; // the buttons are not clickable
    float: left;
    margin-right: 10px;
    font-size: 12px;
    color: darken($board-primary, 20%);
    font-weight: bold;
    cursor: pointer;
    box-sizing: border-box;
    border: 1px solid transparent;
    position: relative;

    &:hover {
      color: darken($board-primary, 25%);

      &::before {
        content:"";
        display: block;
        position: absolute;
        width: 100%;
        height: 100%;
        background: rgba(255,255,255,0.2);
        clip-path: polygon(50% 100%, 15% 73%, 37% 73%, 37% 0%, 63% 0%, 63% 73%, 85% 73%);
      }
    }

    span {
      position: relative;
    }
  }

  .slot-circle {
    margin: 10px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    float: left;
    background: rgba(0,0,0,0.3);
    box-shadow: inset 3px 3px 3px rgba(0,0,0,0.3), inset -1px -1px 2px rgba(0,0,0,0.125);
    pointer-events: none;
  }
}

.valid {
  width: 40px;
  height: 40px;
  opacity: 1;
  overflow: hidden;
  padding-left: 10px;
  position: relative;
  boz-sizing: border-box;
}

[class^=correct], [class^=partial], [class^=empty] {
  background: #222;
  display: none;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  float: left;
  box-sizing: border-box;
  margin: 2px;
  box-shadow: inset -1px -1px 0 rgba(0,0,0,1),inset -1px -1px 4px rgba(255,255,255,0.15), inset 1px 1px 0 rgba(255,255,255,0.2), inset 1px 1px 4px rgba(0,0,0,0.2), 1px 1px 2px rgba(0,0,0,0.4);
}

[class^=empty] {
  width: 20px;
  height: 20px;
  background: rgba(0,0,0,0.4);
  display: inline-block;
  margin: 0;
  border: 4px solid $board-primary;
  box-shadow: inset 2px 2px 4px rgba(0,0,0,0.3);;
}

[class^=partial] {
  background: white;
  box-shadow: inset -1px -1px 0 rgba(0,0,0,0.1), inset 1px 1px 0 #fff, inset 1px 1px 4px rgba(0,0,0,0.2), 1px 1px 2px rgba(0,0,0,0.4);
}

#info {
  width: 24px;
  height: 24px;
  background: #369;
  border-radius: 50%;
  position: fixed;
  top: 5px;
  left: 5px;
  z-index: 1;
  line-height: 24px;
  text-align: center;
  display: inline-block;
  color: white;
  font-style: italic;
  font-family: serif;
  cursor: pointer;

  &:hover {
    background: #47a;
  }
}

#init {
  display: flex;
}

.popup > div {
  clip-path: polygon(0% 2%, 2% 3%, 0% 0%, 3% 2%, 2% 0%, 50% 1%, 98% 0%, 97% 2%, 100% 0%, 98% 3%, 100% 2%, 99% 50%, 100% 98%, 98% 97%, 100% 100%, 97% 98%, 98% 100%, 50% 99%, 2% 100%, 3% 98%, 0% 100%, 2% 97%, 0% 98%, 1% 50%);
  max-height: 80vh;
  overflow: auto;
}

#init #start-button + label {
  display: none;
}

[id^=round-]:checked ~ #init,
[id^=round-]:checked ~ #init .games,
[id^=round-]:checked ~ #init #start-button {
  display: none;
}

#hideInit:checked + .popup,
[id^=round-]:checked ~ #init #start-button + label {
  display: flex;
}

.popup {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  align-items: center;
  justify-content: center;
  z-index: 1;
  background: rgba(0,0,0,0.2);
  display: none;

  &:target {
    display: flex;
  }

  & > div {
    background: white;
    padding: 30px;
    display: inline-block;
    text-align: left;
    border-radius: 5px;
    max-width: 400px;
    margin: 10px;
  }

  & label {
    display: inline-block;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: white;
    cursor: pointer;
  }

  & h2 {
    margin: 0;
    margin-bottom: 10px;
  }

  & .close {
    display: inline-block;
    background: #369;
    color: white;
    padding: 5px 15px;
    text-decoration: none;
    border-radius: 2px;
    float: right;
    margin-top: 10px;
    white-space: nowrap;
    height: auto;
    width: auto;

    &:hover {
      background: #47a;
    }
  }
}

.games {
  display:flex; 
  width: 100%; 
  justify-content: space-between;
  flex-wrap: wrap;

  & label {
    width: auto;
    height: auto; 
  }
}

// all the game buttons will have their own ID
@for $i from 1 through length($fullcodes) {
  $code: nth($fullcodes, $i);
  .games label:nth-child(#{$i})::after {
    content: " #{$i}";
  }
  #code-#{$code}:checked ~ #init .games label[for="code-#{$code}"] {
    box-shadow: 0 0 0 2px white, 0 0 0 4px #369;
  }
}

label.green-button,
input.green-button {
  border: 0;
  font-family: Roboto, Arial, sans-serif;
  font-size: 16px;
  border-radius: 2px;
  padding: 10px 15px;
  text-align: center;
  box-sizing: border-box;
  width: 100%;
  height: auto;
  margin-top: 10px;
  background: #0a0;
  color: white;
  text-transform: uppercase;
}

// styling of the code pegs in the selection popup
// thanks @racascou for the suggestions!
.color-labels label,
.round-container a {
  position: relative;

  &::after {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 14px;
    text-shadow: -0.6px -1px 1px rgba(255,255,255,0.4), 1px 0.25px 1px rgba(0,0,0,0.5);
    padding: 4px;
    border: 2px dotted rgba(255,255,255,0.15);
    border-radius: 50%;
    width: 24px;
    height: 24px;
    line-height: 24px;
    text-align: center;
  }
}
@each $color in $color-number {
  .color-labels label:nth-child(#{$color}) {
    box-shadow: inset 1px 1px 1px rgba(255, 255, 255, 0.3), inset -1px -1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 4px #{nth($colors, $color)}, inset 3px 3px 6px rgba(0, 0, 0, 0.3), inset -3px -3px 6px rgba(255, 255, 255, 0.3), 0 0 1px black, 1px 1px 2px black;
    background: #{nth($colors, $color)};

    &::after {
      content: "#{nth($color-letters, $color)}";
      color: #{nth($colors, $color)};
    }
  }
}

#color-blind:not(:checked) ~ #board .color-labels label::after {
  display: none;
}

/* Game Logic */
@each $round in $rounds {
  // make the buttons clickable only if current round
  #round-#{$round}:checked ~ div.round-#{$round} {
    label, a {
      visibility: visible;
      pointer-events: auto;
    }
  }

  // detect if a correct answer was given and show win messages
  @each $code in $codes {
    #code-#{nth($code, 1)}#{nth($code, 2)}#{nth($code, 3)}#{nth($code, 4)}:checked ~ input#completed-#{$round}:checked {
      + input[name=round]:checked ~ [name^="slot-1"][value="#{nth($code, 1)}"]:checked ~ [name^="slot-2"][value="#{nth($code, 2)}"]:checked ~ [name^="slot-3"][value="#{nth($code, 3)}"]:checked ~ [name^="slot-4"][value="#{nth($code, 4)}"]:checked {
        ~ .popup {
          display: none;
        }
        ~ .won {
          display: flex;
        }
        ~ #confetti {
          display: block;
        }
      }

      @each $slot in $slots {
        // detects each correcta answer and displays the black dot
        ~ [name="slot-#{$slot}-round-#{$round}"][value="#{nth($code, $slot)}"]:checked ~ .round-container .valid-#{$round} .correct-#{$slot} {
          display: inline-block;
        }

        // detetcs partial answers and displays white dot
        ~ [name="slot-#{$slot}-round-#{$round}"][value="#{nth($code, $slot)}"]:not(:checked) ~ [name^=slot][name$="round-#{$round}"][value="#{nth($code, $slot)}"]:checked ~ .round-container .valid-#{$round},
        ~ [name^=slot][name$="round-#{$round}"][value="#{nth($code, $slot)}"]:checked ~ [name="slot-#{$slot}-round-#{$round}"][value="#{nth($code, $slot)}"]:not(:checked) ~ .round-container .valid-#{$round} {
          .partial-#{$slot} {
            display: inline-block;
          }
        }
      }
    }
  }
}

@each $round in $rounds {  
  #completed-#{$round}:checked + input[name=round]:checked ~ #transition-#{$round} {
    display: flex;
  }

  input#completed-#{$round}:checked,
  input#round-#{$round}:checked {
    @each $slot in $slots {
      ~ [name="slot-#{$slot}-round-#{$round}"]:checked ~ .round-container.round-#{$round} .slot-circle:nth-child(#{$slot + 1}) {
        width: 36px;
        height: 36px;
        margin: 2px;
        border: 0;
      }

      @each $color in $color-number {
        ~ [name="slot-#{$slot}-round-#{$round}"][value="#{$color}"]:checked ~ .round-container.round-#{$round} .slot-circle:nth-child(#{$slot + 1}) {

          box-shadow: inset 1px 1px 1px rgba(255, 255, 255, 0.3), inset -1px -1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 4px #{nth($colors, $color)}, inset 3px 3px 6px rgba(0, 0, 0, 0.3), inset -3px -3px 6px rgba(255, 255, 255, 0.3), 0 0 2px rgba(0,0,0,0.35), 1px 1px 2px rgba(0,0,0,0.5);
          background: #{nth($colors, $color)};

          #color-blind:checked ~ #board &::after {
            content: "#{nth($color-letters, $color)}";
            color: #{nth($colors, $color)};
          }
        }
      }
    }
  }
}
View Compiled
// No JS :)

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.