<h1>Tangram Memory Game</h1>
<p>Click cards and find matching tangrams</p>
<div class="board" id="tangramgrid"></div>
<svg style="display:none" viewBox="0 0 100 100">
  <defs>
    <polygon id="tangram1" points="0 0, 50 50, 0 100" />
    <polygon id="tangram2" points="50 50, 75 25, 100 50, 75 75" />
    <polygon id="tangram3" points="0 100, 25 75, 75 75, 50 100"/>
  </defs>
</svg>
*, *:before, *:after {
  box-sizing: border-box;
}

body {
  padding: 1rem;
  min-height: 100vh;
  border: 1rem solid;
  background: #926ea9;
  color: wheat;
  text-align: center;
}

h1 { 
  margin: 0;
  font-weight: 400;
  font-family: "Ribeye Marrow", sans-serif;
}
p {
  font-family: Montserrat, sans-serif;
  font-weight: 300;
  margin: 0 0 1.5rem;
}
.t svg {
  display: block
}
.t use {
  transition: .5s;  
}
.t:not(.revealed) use {
  fill: darken(#926ea9, 10);
}

use {
  stroke: wheat;
}
.revealed use {
  stroke: transparent;
}

.board {
  max-width: 75vh;
  margin: 0 auto;
  display: grid;
  grid-gap: .5rem;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 1fr;
}
.t {
  border: 1px solid; 
}

.revealed {
  background: white;
  border-color: white;
}

.t {
  .bt-1 {transform: translate(0,0);}
  .bt-2 {transform: translate(0,0) scale(-1,1) rotate(90deg);}
  .st-1 {transform: translate(25px,75px) scale(.5,.5) rotate(-90deg);}
  .mt {transform: translate(100px,49.5px) scale(.7142,.7142) rotate(45deg);}
  .st-2 {transform: translate(100px,50px) scale(.5,.5) rotate(-180deg);}
  .sq {}
  .rh {}
}

.tcat.revealed {
  svg {transform: translate(0,8px);}
  .bt-1 {
     transform: translate(12.5px,10px);
     fill: #3d3d3d;
  }
  .bt-2 {
    transform: translate(-8.5px,130px) scale(-1,1) rotate(135deg);
    fill: #666666;
  }
  .st-1 {
    transform: translate(25px,0px) scale(.5,.5) rotate(180deg);
  }
  .st-2 {
    transform: translate(-25px,-50px) scale(.5,.5) rotate(0deg);
  }
  .st-1, .st-2 {
    fill: #FFE6C5;
  }
  .mt {
    transform: translate(12.5px,12.5px) scale(-.7142,.7142) rotate(0deg);
  }
  .mt, .rh {
    fill: #CCCCCC;
  }
  .sq {
    transform: translate(-75px,-50px);
    fill: #303030;
  }
  .rh {
    transform: translate(133px,200px) scale(1,-1) rotate(45deg);
  }
}

.tsquirrel.revealed {
  svg {transform: translate(0,12px);}
  .bt-1 {
     transform: translate(12.5px,-12.5px);
     fill: #6B3010;
  }
  .bt-2 {
    transform: translate(-8.5px,108.5px) scale(-1,1) rotate(135deg);
    fill: #7F381C;
  }
  .st-1 {
    transform: translate(12.5px,87.5px) scale(.5,.5) rotate(180deg);
  }
  .st-2 {
    transform: translate(97.5px,108.5px) scale(.5,.5) rotate(135deg);
  }
  .mt {
    transform: translate(12.5px,-35px) scale(.7142,.7142) rotate(45deg);
  }
  .sq {
    transform: translate(22.5px,-16.5px);
    fill: #E6E6E6;
  }
  .rh {
    transform: translate(172.5px,33.25px) scale(1,1) rotate(90deg);
    fill: #7F381C;
  }
  .st-1, .st-2, .mt {
    fill: #8C6239;
  }
}

.theart.revealed {
  .bt-1 {
     transform: translate(-25px,50px) rotate(-90deg);
     fill: #D4145A;
  }
  .bt-2 {
    transform: translate(25px,50px) scale(-1,1) rotate(90deg);
    fill: #c1272d;
  }
  .st-1 {
    transform: translate(50px,25px) scale(.5,.5) rotate(-90deg);
    fill: #9e005d;
  }
  .st-2 {
    transform: translate(25px,50px) scale(.5,.5) rotate(0deg);
    fill: darken(#ed1c24, 6);
  }
  .mt {
    transform: translate(-25px,50px) scale(.7142,.7142) rotate(-45deg);
    fill: #ed1c24;
  }
  .sq {
    transform: translate(-25px,50px)  ;
    fill: lighten(#c1272d, 1);
  }
  .rh {
    transform: translate(50px,125px) scale(1,-1) rotate(0deg) ;
    fill: #D4145A;
  }
}

.trocket.revealed {
  .bt-1 {
     transform: translate(75px,75px) rotate(-180deg);
     fill: #ED1C24
  }
  .bt-2 {
    transform: translate(75px,75px) scale(-1,1) rotate(180deg);
    fill: #C1272D;
  }
  .st-1 {
    transform: translate(-37.5px,62.5px) scale(.5,.5) rotate(-90deg);
    fill: #FBB03B;
  }
  .st-2 {
    transform: translate(37.5px,37.5px) scale(.5,.5) rotate(90deg);
    fill: #BDCCD4;
  }
  .mt {
    transform: translate(125px,25px) scale(-.7142,.7142) rotate(-135deg);
    fill: #E6E6E6;
  }
  .sq {
    transform: translate(-37.5px,12.5px)  ;
    fill: #4D4D4D;
  }
  .rh {
    transform: translate(137px,137px) scale(1,-1) rotate(90deg);
    fill: #FBB03B;
  }
}

.tbird.revealed {
  .bt-1 {
     transform: translate(72.5px,-25px) rotate(45deg);
     fill: #D5ABD8;
  }
  .bt-2 {
    transform: translate(72.5px,45.5px) scale(1,1) rotate(-135deg);
    fill: #D889DB;
  }
  .st-1 {
    transform: translate(1.5px,45.5px) scale(.5,.5) rotate(-45deg);
    fill: #E6E6E6;
  }
  .st-2 {
    transform: translate(2px,-25px) scale(.5,.5) rotate(45deg);
    fill: #FBB03B;
  }
  .mt {
    transform: translate(2px,-26px) scale(.7142,.7142) rotate(0deg);
    fill: #D29E98;
  }
  .sq {
    transform: translate(36.5px,-25px) rotate(45deg);
    fill: #E6E6E6;
  }
  .rh {
    transform: translate(107px,10px) scale(1,1) rotate(45deg);
    fill: #F2B1B7;
  }
}

.thome.revealed {
  .bt-1 {
     transform: translate(25px,66.5px) rotate(-90deg);
     fill: #D54D1D;
  }
  .bt-2 {
    transform: translate(0px,116.5px) scale(1,1) rotate(-90deg);
    fill: #F8CC88;
  }
  .st-1 {
    transform: translate(100px,66.5px) scale(.5,.5) rotate(90deg);
    fill: #FBE098;
  }
  .st-2 {
    transform: translate(100px,116.5px) scale(.5,.5) rotate(180deg);
    fill: #F8CC88;
  }
  .mt {
    transform: translate(50px,66.5px) scale(-.7142,.7142) rotate(-45deg);
    fill: #FBE098;
  }
  .sq {
    transform: translate(25px,-75px) rotate(45deg);
    fill: #E6E6E6;
  }
  .rh {
    transform: translate(61px,137px) scale(1,-1) rotate(45deg);
    fill: #E9624E;
  }
}

.tlotus.revealed {
  .bt-1 {
    transform:translate(50px,85.5px) rotate(135deg);
    fill: #CDDC39;
  } 
  .bt-2 {
    transform:translate(121.5px,14.5px) scale(1,1) rotate(45deg);
    fill: #8BC34A;
  } 
  .st-1{
    transform: translate(50px,50px) scale(.5,.5) rotate(135deg);
    fill: #d889db;
  }
  .mt {
    transform: translate(86px,50px) scale(.7142,.7142) rotate(90deg);
    fill: #4CAF50;
  }
  .st-2 {
    transform: translate(50px,50px) scale(-.5,.5) rotate(135deg);
    fill: #f2b1b7;
  }
  .sq {
    transform: translate(-25px,-25px);
    fill: #de5e87;
  }
  .rh {
    transform:  translate(121px,14.5px) rotate(45deg);
    fill: #009688;
  }
}
.tboat.revealed {
  .bt-1 {
    transform: translate(50px, 12.5px) rotate(45deg);
    fill: #eeeeee;
  }
  .bt-2 {
    transform: translate(50px, 0) scale(1, 1) rotate(0deg);
    fill: #dddddd;
  }
  .st-1 {
    transform: translate(50px, 100px) scale(0.5, 0.5) rotate(-90deg);
    fill: #ededed;
  }
  .mt {
    transform: translate(100px, 100px) scale(0.7142, 0.7142) rotate(90deg);
    fill: #795548;
  }
  .st-2 {
    transform: translate(85px, 0px) scale(0.5, 0.5) rotate(135deg);
    fill: #d27373;
  }
  .sq {
    transform: translate(25px, 25px);
    fill: #c7c4c4;
  }
  .rh {
    transform:translate(64px,29.5px) rotate(45deg);
    fill: darken(#795548,10);
  }
}

.t {
  transition: opacity .75s .75s, background .5s;
  cursor: pointer;
  opacity: 1;
}

.hidden {  
  cursor: auto;
  opacity: .5;
}
View Compiled
const classes = [
	'tcat',
	'tsquirrel',
	'theart',
	'trocket',
	'tbird',
	'thome',
	'tlotus',
	'tboat'
];

const tangram = `
<div class="t">
  <svg viewbox="-50 -50 200 200" >
    <use class="bt-1" xlink:href="#tangram1" transform="translate(0,0)"/>
    <use class="bt-2 red" xlink:href="#tangram1" transform="translate(0,0) scale(-1,1) rotate(90 0 0)"/>
    <use class="st-1" xlink:href="#tangram1" transform="translate(25,75) scale(.5,.5) rotate(-90 0 0)"/>
    <use class="mt" xlink:href="#tangram1" transform="translate(100,49.5) scale(.7142,.7142) rotate(45 0 0)"/>
    <use class="st-2" xlink:href="#tangram1" transform="translate(100,50) scale(.5,.5) rotate(-180 0 0)"/>
    <use class="sq" xlink:href="#tangram2"/>
    <use class="rh" xlink:href="#tangram3"/>
  </svg>
</div>
`
class MemoryGame {
	constructor (selector) {
		this.selector = selector
		this.init()
	}
	init () {
		this.randomize()
		this.buildGrid()
		this.revealed = []	    
	}
	randomize () {
		this.classes = this.shuffle(classes.concat(classes))
	}
	shuffle (array) {		
		array.sort( function(a, b) {return 0.5 - Math.random()} );
		return array;
	}
	buildGrid () {
		let html = ''
		for (let i = 0; i<this.classes.length; i++ ) {
			html += tangram;
		}
		this.selector.innerHTML = html;
		this.cards = this.selector.querySelectorAll('.t')
		this.classes.forEach ( (el, i) => {
			const card = this.cards[i]
			card.classList.add(el)
			card.setAttribute( 'data-class', el)
			this.addCardListeners(card)
		})

	}
	checkMatch () {
		return this.revealed.length === 2 && this.revealed[0].getAttribute('data-class') === this.revealed[1].getAttribute('data-class')
	}
	addCardListeners (el) {

		el.addEventListener( 'mouseenter', (e) => {

			if (el.classList.contains('revealed')){
				return
			}
			
			if ( this.revealed.length < 2 ) {
				return
			}

			Array.prototype.slice.call(this.revealed).forEach( (item) => {
				item.classList.remove('revealed')
			})
			this.revealed = []
		})

		el.addEventListener( 'click', (e) => {

			if (el.classList.contains('revealed')) {
				return
			}
			
			el.classList.add('revealed');
			this.revealed.push(el)
			if ( this.checkMatch() ) {
				Array.prototype.slice.call(this.revealed).forEach( (item) => {
				  item.classList.add('hidden')
				  this.revealed = []
			  })
			}

		}, false );

	}
}

let memory = new MemoryGame(document.getElementById("tangramgrid"));
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.