Live site: https://jeanwll.github.io/ConnectFour/
This is a solution to the Connect Four game challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
Users should be able to:
- View the game rules
- Play a game of Connect Four against another human player (alternating turns on the same computer)
- View the optimal layout for the interface depending on their device's screen size
- See hover and focus states for all interactive elements on the page
- See the discs animate into their position when a move is made
- Play against the computer
- Control the game with keyboard (Arrow keys and SpaceBar)
- Object Oriented JavaScript
- CSS components workflow
- Flexbox
I experimented displaying game data with pseudo elements content property, allowing simpler markup and CSS selectors controlling content format.
body[data-player='2'] .timer:before {
  content: 'Player 2\'s turn';
}
body[data-pvc][data-player='2'] .timer:before {
  content: 'CPU\'s turn';
}
.timer:after {
  content: attr(data-value) 's';
}<button class="btn-play | game-btn">Play</button>body[data-state='end'] .btn-play:after {
  content: ' Again';
}The project included 3 distinct layouts, the grid had to keep an aspect-ratio and all the UI should fit in 100% of the height unlike usual webpages.
The game should perfectly fit any viewport above 360x580.
@media (max-aspect-ratio: 840 / 580) and (max-width: 1079px) {
  @media (max-width: 559px), (max-height: 859px) {
    @media (max-width: 380px) and (max-height: 663px) {
    }
    @media (min-width: 381px), (min-height: 664px) {
    }
  }
  @media (min-width: 560px) and (min-height: 860px) {
  }
}
@media not all and (max-aspect-ratio: 840 / 580), (min-width: 1080px) {
}Future media queries will make this much simpler:
@when (aspect-ratio <= 840 / 580) and (width <= 1079px) {
  @when (width <= 559px), (height <= 859px) {
    @when (width <= 380px) and (height <= 663px) {
    }
    @else {
    }
  }
  @else {
  }
}
@else {
}Flexbox was very handy for resizing UI proportionally on different viewports.
flex-basis is easier to implement from figma, more explicit and accurate than height%.
.game__header {
  flex-basis: 40px;
}
.scores {
  flex-basis: 81px;
}
.grid {
  flex-shrink: 0;
}An other tool for supporting different viewports was clamp().
Let's say I wanted a font-size to progressively reduce from 56px at viewport height = 880px to 32px at viewport height = 580px.
You can calculate the linear equation from 2 points (880, 56) and (580, 32) with the formula or an online calculator and get the result in vh + px format.
font-size: clamp(32px, calc(20vh - 88px), 56px);It was also a good opportunity to use min() instead of height and min-height.
.game__wrapper {
  height: min(807px, calc(100% - 20px));
  aspect-ratio: 632 / 807;
}I used the combination of margin-inline-start and direction to make my CSS less redundant.
.score {
  margin-inline-start: 24px;
}
.score2 {
  direction: rtl;
}I used :is() to make some selectors shorter.
:is(body:not([data-state='playing']), body[data-dropping]) .column--selected:before  {
  opacity: 0;
}I used :where() to impose a low specifity.
The same .info element is used for text before game starts and after game ends.
body[data-state="start"] .info:before {
  content: 'Player 1 starts';
}
body:where([data-pvc][data-player="2"]) .info:before {
  content: 'CPU';
}The current "CPU Opponent" only has a basic defensive strategy. Advanced algorithm for perfect play exists but would leave no chance for the player. Maybe we could have a submenu for CPU strength selection "Easy, Medium, Hard".
Having the game work with a shareable link to play Online PVP could be interesting! We could use the same submenu to select either "Local PVP" or "Online PVP".

