Game Cards in CSS

Game Cards in CSS
Project: Dev Cards
Author: Mariusz Dabrowski
Edit Online: View on CodePen
License: MIT

This code snippet helps you to create a game card. It loads an image with the source URL specified in the “src” attribute. The image is of an arm with a blue sleeve, and it has the CSS class “arm.” The second code block creates a div element with the class “mat” that contains an image with the “not-for-sale” class and a div with the class “cards.” The “not-for-sale” image is also sourced from an external URL and is displayed inside the “mat” div. The “cards” div is empty, indicating that it will be populated with content dynamically via JavaScript.

How to Create Game Cards in CSS

First of all, load the following assets into the head tag of your HTML document.

<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Kreon:400,700&display=swap'>

Create the HTML structure for the game cards as follows:

<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-arm.png" alt="Arm with blue sleeve" class="arm">
<div class="mat">
  <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-not-for-sale.png" alt="Not for sale" class="not-for-sale">

  <div class="cards"></div>
</div>

Now, style the game cards using the following CSS styles:

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  background: #020203;
  margin: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  padding: 0 100px 100px;
  font-family: "Kreon", serif;
}

@media (max-width: 974px) {
  body {
    padding: 0 20px 40px 20px;
  }
}

/* --- */
/* Mat */
/* --- */

.mat {
  width: 100%;
  background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-mat.png")
    left bottom repeat-y;
  background-size: 100% auto;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 140px 20px 50px;
}

@media (max-width: 974px) {
  .mat {
    padding: 40px 20px 50px;
  }
}

.mat::after {
  position: absolute;
  top: 100%;
  left: 0;
  content: "";
  background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-mat-cap.png")
    0 0 no-repeat;
  background-size: 100% auto;
  width: 100%;
  padding-bottom: 3.1035873%;
}

.not-for-sale {
  position: absolute;
  top: 60px;
  left: calc(100% - 14px);
  width: 70px;
}

@media (max-width: 974px) {
  .not-for-sale {
    display: none;
  }
}

/* --- */
/* Arm */
/* --- */

.arm {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  width: 200px;
  pointer-events: none;
  transition: transform 1.5s cubic-bezier(0.165, 0.84, 0.44, 1);
}

/* ----- */
/* Cards */
/* ----- */

.cards {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
}

.card {
  width: 200px;
  margin: 20px;
  position: relative;
  transition: transform 0.3s;
}

@media (max-width: 779px) {
  .card {
    margin: 10px;
  }
}

.card:hover {
  transform: scale(1.2);
  transition: transform 0.15s;
  cursor: pointer;
}

.card__background {
  width: 100%;
}

.card__banner {
  width: 100%;
  position: absolute;
  top: 2.5%;
  left: 50%;
  transform: translateX(-51.5%);
  background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-banner-gold.png")
    0 0 no-repeat;
  background-size: 100% auto;
  z-index: 20;
}

.card__banner__text {
  width: 80%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-51%, 15%);
  z-index: 30;
  text-align: center;
  font-size: 16px;
  font-weight: 600;
  color: #ffffff;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.7);
}

.card__image {
  background: red;
  width: 82%;
  position: absolute;
  left: 47%;
  transform: translateX(-50%);
  z-index: 10;
}

.card--square .card__image {
  top: 13%;
}
.card--round .card__image {
  top: 2%;
}
.card--pointy .card__image {
  top: 13.5%;
  width: 79%;
  left: 46.5%;
}

.card__image__border {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 20;
}

.card__image__svg {
  width: calc(100% - 2px);
  position: absolute;
  top: -10px;
  left: 2px;
  z-index: 10;
}

.card--square .card__image__svg {
  top: -5px;
}
.card--round .card__image__svg {
  top: -0px;
}
.card--pointy .card__image__svg {
  top: -4px;
}

.card__image__sparkles {
  mix-blend-mode: color-dodge;
}

.card__description {
  width: 80%;
  height: 37%;
  position: absolute;
  bottom: 11%;
  left: 50%;
  transform: translateX(-53%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px;
  color: #eae3d1;
  font-size: 14px;
  line-height: 1.2;
  text-align: center;
  font-weight: 400;
  text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);
}

.card__description strong {
  color: #efc851;
  font-weight: 400;
}

.card__orb {
  width: 23%;
  position: absolute;
  top: -4%;
  left: -6%;
  z-index: 50;
}

.card__orb__image {
  width: 100%;
}

.card__orb__value {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin-top: -3px;
  margin-left: -1px;
  width: 40%;
  opacity: 0.9;
}

Finally, add the following JavaScript function:

const usersData = [
{
  name: "Banjo Chris",
  photo: "chris-coyier.jpg",
  description:
  "The banjo plays, giving you and your party new life. <strong>You are invincible next turn.</strong>",
  shiny: false,
  twitter: "https://twitter.com/chriscoyier" },

{
  name: "Gentleman Shaw",
  photo: "stephen-shaw.jpg",
  description:
  "You wouldn't harm a thing, so instead you charm the enemy. <strong>They join your party.</strong>",
  shiny: true,
  twitter: "https://twitter.com/shshaw" },

{
  name: "Keycaps Cassidy",
  photo: "cassidy-williams.jpg",
  description:
  "The MX Blue clickity clacks penetrate the enemy eardrums. <strong>Deal 56 damage.</strong>",
  shiny: false,
  twitter: "https://twitter.com/cassidoo" },

{
  name: "Piano Khourshid",
  photo: "david-khourshid.jpg",
  description:
  "Forgot his piano, resorts to reading sheet music aloud. <strong>Enemy falls asleep.</strong>",
  shiny: false,
  twitter: "https://twitter.com/DavidKPiano" }];



function initMouseEvents() {
  const cards = document.querySelectorAll(".card");
  const arm = document.querySelector(".arm");

  for (let i = 0; i < cards.length; i++) {
    cards[i].addEventListener("mouseenter", mouseEnter);
    cards[i].addEventListener("mouseleave", mouseLeave);
  }

  function mouseEnter(e) {
    // If you end up using this, don't do this on every mouse enter as it's not very performant
    // Do it once outside of this function and reference it
    const targetInfo = e.target.getBoundingClientRect();

    arm.removeAttribute("style");
    arm.style.transform = `translate(${targetInfo.left -
    document.body.clientWidth / 2}px, ${targetInfo.top +
    document.documentElement.scrollTop +
    50}px)`;
  }

  function mouseLeave() {
    arm.removeAttribute("style");
    arm.style.transition = `transform 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)`;
  }
}

// --------------
// Generate Cards
// --------------

const shapes = ["round", "square", "pointy"];
const shapePaths = {
  round: '<ellipse cx="130" cy="111.1" rx="130" ry="111.1"/>',
  square:
  '<polygon points="0,0 0,158.7 15.3,172.7 239.3,172.7 251.7,160.3 251.7,0 "/>',
  pointy:
  '<polygon points="0,0 0,141.3 105.3,177.3 149.3,177.3 250.7,142 250.7,0 "/>' };

const cardColors = ["green", "red", "black"];
const bannerColors = ["gold", "blue", "black"];
const cardsContainer = document.querySelector(".cards");

class Card {
  constructor({
    name,
    photo,
    description,
    shiny,
    shape,
    cardColor,
    bannerColor,
    value,
    twitter })
  {
    this.name = name;
    this.photo = photo;
    this.description = description;
    this.shiny = shiny;
    this.shape = shape;
    this.cardColor = cardColor;
    this.bannerColor = bannerColor;
    this.value = value;
    this.shiny = shiny;
    this.twitter = twitter;
  }

  getTemplate() {
    return `
    <a href="${this.twitter}" target="_blank" class="card card--${this.shape}">
      <div class="card__orb">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-orb-${
    this.cardColor
    }.png" alt="" class="card__orb__image">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-${
    this.value
    }.png" alt="" class="card__orb__value">
      </div>
      <div class="card__banner">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-banner-${
    this.bannerColor
    }.png" alt="" class="card__banner">
        <div class="card__banner__text">${this.name}</div>
      </div>
      <div class="card__image">
        <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-frame-${
    this.shape
    }-${this.bannerColor}.png" alt="" class="card__image__border">
        <svg class="card__image__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 260 222.2" style="enable-background:new 0 0 260 222.2;" xml:space="preserve">
          <clipPath id="mask-${this.shape}">
            ${shapePaths[this.shape]}
          </clipPath>
          <image clip-path="url(#mask-${
    this.shape
    })" height="100%" width="100%" ${
    this.shape === "round" ? 'y="20"' : ""
    } xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-${
    this.photo
    }" />
          ${
    this.shiny ?
    `<image clip-path="url(#mask-${
    this.shape
    })" width="100%" xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-sparkles.gif" class="card__image__sparkles" />` :
    ``
    }
        </svg>
      </div>
      <div class="card__description">
        <div class="card__description__text">
            ${this.description}
        </div>
      </div>
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/hand-card-back-${
    this.cardColor
    }__${this.shape}.png" alt="" class="card__background">
    </a>
    `;
  }}


shuffle(usersData);

usersData.forEach((user, index) => {
  const newCard = new Card({
    name: user.name,
    photo: user.photo,
    description: user.description,
    shiny: user.shiny,
    shape: getRandomArrayValue(shapes),
    cardColor: getRandomArrayValue(cardColors),
    bannerColor: getRandomArrayValue(bannerColors),
    value: Math.floor(Math.random() * 9),
    twitter: user.twitter });


  cardsContainer.innerHTML += newCard.getTemplate();
});

initMouseEvents();

// -------
// Helpers
// -------

function getRandomArrayValue(array) {
  const randomValue = Math.floor(Math.random() * array.length);
  return array[randomValue];
}

function shuffle(array) {
  var currentIndex = array.length,
  temporaryValue,
  randomIndex;

  while (0 !== currentIndex) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

That’s all! hopefully, you have successfully created game cards in CSS. If you have any questions or suggestions, feel free to comment below.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply