<div id="app">
	<div 
		:class="{'poke-classic': classic}"
		class="container"
	>
		<transition name="animate-section">
			<section 
				v-if="!isPlaying && !isDone" 
				class="poke-section"
			>
				<h2>What type of trainer are you?</h2>
				<div class="poke-intro-trainer">
					<div class="poke-ball"></div>
					<img 
						:class="{active: trainerHovered === 'classic'}"
						class="poke-trainer-img poke-trainer-img-classic"
						src="https://raw.githubusercontent.com/tiffachoo/pokesprites/master/trainers/red-rb.png" 
						alt="Trainer red"
					>
					<img 
						:class="{active: trainerHovered === 'master'}"
						class="poke-trainer-img poke-trainer-img-master"
						src="https://raw.githubusercontent.com/tiffachoo/pokesprites/master/trainers/red-sm.png" 
						alt="Trainer red again"
					>
				</div>
				<button 
					class="button spacer"
					@click="startGame(151)"
					@mouseover="trainerHover('classic')"
					@mouseout="trainerHover"
				>
					Classic
				</button>
				<button 
					class="button"
					@click="startGame(0)"
					@mouseover="trainerHover('master')"
					@mouseout="trainerHover"
				>
					Master
				</button>
			</section>
		</transition>
		
		<transition name="animate-section">
			<section
				v-if="isPlaying"
				class="poke-section"
			>
				<h1 class="poke-title">
					Who's that pokemon?
				</h1>
				<div class="poke-question-wrapper">
					<span class="poke-question">
						<span class="poke-question-number">
							{{ question }} 
						</span>
						<span class="poke-question-amount">
							/ {{ questionAmount}}
						</span>
					</span>
					<span class="poke-score">
						{{ score }}
						<small>pts</small>
					</span>
					<div 
						class="poke-image"
						:class="{'poke-image-success': isChecked && selected.name === answer.name, 'poke-image-error': isChecked && selected.name !== answer.name}"
					>
						<img 
							:src="image" 
							alt="No cheating"
							class="poke-image-img"
						>
					</div>
					<transition-group 
						tag="div"
						name="animate-options"
						:class="{'poke-options-answers': isChecked}"
						class="poke-options"
					>
						<button 
							v-for="(pokemon, index) in options"
							:key="pokemon.name"
							:data-index="index"
							:class="{'selected': selected.index === index, 'success': isChecked && pokemon.name === answer.name , 'error': isChecked && selected.index === index && selected.name !== answer.name}"
							class="poke-options-button"
							@click="selectAnswer(pokemon.name, index)"
						>{{ pokemon.name | prettifyName }}</button>
					</transition-group>
					<footer class="poke-buttons">
						<button
							:disabled="isChecked || Object.keys(selected).length < 1"
							class="button"
							@click="checkAnswer"
						>Submit</button>
						<button 
							:disabled="!isChecked"
							class="button"
							@click="getNextQuestion"
						>Next</button>
					</footer>
				</div>
			</section>
		</transition>
		
		<transition name="animate-section">
			<section
				v-if="isDone"
				class="poke-final"
			>
				<h2>Final score</h2>
				<span class="poke-final-score">
					<span class="poke-final-score-number">{{ score }}</span>
					pts
				</span>
				<button 
					class="button"
					@click="restartGame"
				>Play again</button>
			</section>
		</transition>
	</div>
</div>
@import 'https://fonts.googleapis.com/css?family=VT323';
$black: #333;
$white: #fff;

$primary-color: #f65a52;
$primary-color-dark: darken( $primary-color, 10% );
$primary-color-tint: lighten( $primary-color, 15% );
$primary-font: "VT323", monospace;

$secondary-color: #94acbd;
$secondary-color-dark: darken( $secondary-color, 10% );

$options-color: #c5d5ee;
$options-color-hover: darken( $options-color, 7% );
$success-color: #7bd55a;
$error-color: #ff8b62;

$border-radius-main: 1rem;
$border-width: 6px;
$border-main: solid $border-width $black;
$options-height: 48px;

* { box-sizing: border-box; }
body {
	background-color: $primary-color;
	font-family: $primary-font;
	font-size: 16px;
	line-height: 1.875em;
	color: $black;
}

.container {
	width: 100%;
	max-width: 400px;
	position: relative;
	margin: 50px auto;
}

h2 {
	font-size: 1.25rem;
	white-space: nowrap;
}

.spacer {
	margin-bottom: 0.5rem;
}

.button {
	padding: 0.5em 1.5em;
	border-radius: $border-radius-main;
	border: solid 1px transparent;
	font-family: $primary-font;
	font-size: 1.5rem;
	background-color: $primary-color-dark;
	color: $black;
	cursor: pointer;
	transition: 0.35s;
		
	&:focus {
		outline: none;
		border: 1px dotted lighten($primary-color, 8%);
	}
	
	&:not([disabled]) {
		&:hover {
			background-color: $black;
			color: $primary-color;
		}
	}
}

.poke-section {
	display: flex;
	flex-direction: column;
	align-items: center;
	position: relative;
	max-width: 500px;
	margin: auto;
}

.poke-intro-trainer {
	position: relative;
	margin-bottom: 1rem;
	height: 200px;
	width: 200px;
	
	.poke-trainer-img {
		position: absolute;
		left: 50%;
		bottom: 0;
		height: 200px;
		opacity: 0;
		transition: 0.4s cubic-bezier(.22,.75,.53,.99);
		
		@media (max-width: 479px) {
			display: none;
		}
			
		&.active {
			transform: translateX(-50%);
			opacity: 1;
		}
		
		&-classic {
			bottom: 5px;
			height: 180px;
			image-rendering: pixelated;
			transform: translateX(-80%);
		}
		
		&-master {
			transform: translateX(-20%);
		}
	}
}

.poke-ball {
	position: absolute;
	top: 50%;
	left: 50%;
	height: 150px;
	width: 150px;
	border-radius: 50%;
	background-color: $primary-color-dark;
	transform: translate(-50%, -50%);
	overflow: hidden;
	
	&::before,
	&::after {
		content: '';
		position: absolute;
	}
	
	&::before {
		z-index: 2;
		top: 50%;
		left: 50%;
		height: 40px;
		width: 40px;
		border-radius: 50%;
		border: solid $border-width $primary-color;
		background-color: $primary-color-tint;
		transform: translate(-50%, -50%);
	}
	
	&::after {
		z-index: 1;
		top: 50%;
		height: 50%;
		width: 100%;
		border-top: solid $border-width $primary-color;
		background-color: $primary-color-tint;
	}
}

.poke-title {
	position: absolute;
	top: -2rem;
}

.poke-question {
	position: absolute;
	right: calc(100% + 0.5rem);
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	
	&-wrapper {
		position: relative;
		width: 250px;
	}
	
	&-number {
		font-size: 8rem;
		line-height: 0.4;
		color: $primary-color-tint;
	}
}

.poke-score {
	position: absolute;
	top: 6rem;
	right: calc(100% + 0.5rem);
	padding-top: 1rem;
	font-size: 1.25rem;
	white-space: nowrap;
	color: $black;
	
	&::before {
		content: '';
		position: absolute;
		top: 0;
		right: 0;
		width: 40px;
		height: $border-width;
		background-color: $black;
	}
}

.poke-image {
	position: relative;
	z-index: 2;
	display: flex;
	justify-content: center;
	align-items: center;
	width: 250px;
	height: 250px;
	border-radius: $border-radius-main;
	border: $border-main;
	background-color: $white;
	overflow: hidden;
	
	&::before,
	&::after {
		content: '';
		position: absolute;
		z-index: -1;
		border-radius: 50%;
	}
	
	&::before {
		width: 100px;
		height: 100px;
		background-color: $options-color;
		opacity: 1;
		transition: 0.65s ease-in-out;
	}
	
	&::after {
		width: 100px;
		height: 100px;
		border: solid ($border-width * 2) $options-color;
		transform: scale(0);
		transition: 0.4s ease-in-out;
	}
	
	&-img {
		width: auto;
		height: 150px;
	}
	
	&-success,
	&-error {
		&::before {
			transform: scale(4);
			opacity: 0.5;
		}
		
		&::after {
			transform: scale(1);
		}
	}
	
	&-success {
		&::before {
			background-color: $success-color;
		}
		
		&::after {
			border-color: $success-color;
		}
	}
	
	&-error {
		&::before {
			background-color: $error-color;
		}
		
		&::after {
			border-color: $error-color;
			width: 10px;
			border-radius: $border-radius-main;
			transform: rotate(45deg);
		}
	}
}

.poke-options {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	z-index: 3;
	top: -30px;
	padding: 0 20px;
	margin: 0 auto;
	width: 170px;
	border-radius: $border-radius-main;
	background-color: $black;
	
	&:not(.poke-options-answers) {
		.poke-options-button {
			&:not(.selected) {
				&:hover {
					background-color: $options-color-hover;
					// color: $primary-color-dark;
					transform: translateY(-$border-width / 2);
				}
		
				&:active {
					&::before {
						transform: translate(-50%, -50%) scale(1);
					}
				}
			}
		}
	}
	
	&.poke-options-answers {
		.poke-options-button {
			cursor: default;
			
			&:not(.error):not(.success) {
				color: $secondary-color;
			}
		}
	}
	
	&-button {
		position: relative;
		width: 100%;
		padding: 0.5em;
		min-width: 200px;
		max-height: $options-height;
		border: $border-main;
		border-radius: $border-radius-main;
		background-color: $options-color;
		font-family: $primary-font;
		font-size: 1.125rem;
		transition: 0.45s;
		cursor: pointer;
		overflow: hidden;
		
		&:focus {
			outline: none;
		}
		
		&::before {
			content: '';
			position: absolute;
			z-index: -1;
			left: 50%;
			top: 50%;
			height: 200px;
			width: 200px;
			border-radius: 50%;
			background-color: $secondary-color;
			transform: translate(-50%, -50%) scale(0);
			transition: 0.2s ease-in-out;
		}
		
		&:not(:last-child) {
			margin-bottom: $border-width / 2;
		}

		&.selected {
			background-color: $secondary-color;
		}
		
		&.error {
			background-color: $error-color;
		}
		
		&.success {
			background-color: $success-color;
		}
	}
}

.poke-buttons {
	text-align: center;
	
	@media (min-width: 480px) {
		position: absolute;
		top: 20px;
		left: 100%;
		
		.button {
			padding-left: calc(1em + 10px);
			border-top-left-radius: 0;
			border-bottom-left-radius: 0;
			transform: translateX(-10px);
		}
	}
	
	.button {
		padding: 1em;
		width: 110px;
		height: 100px;
		color: $white;
		
		&[disabled] {
			color: $primary-color-tint;
			opacity: 0.7;
			cursor: default;
		}
		
		&:not([disabled]) {
			&:hover {
				transform: translateX(0);
			}
		}
		
		&:not(:last-child) {
			margin-bottom: $border-width;
		}
	}
}

.poke-final {
	text-align: center;
	
	&-score {
		display: block;
		position: relative;
		margin-bottom: 1rem;
	
		&::before {
			content: '';
			position: absolute;
			z-index: -1;
			top: 50%;
			left: 50%;
			height: 100px;
			width: 100px;
			border-radius: 50%;
			border: solid ($border-width * 2) $primary-color-dark;
			transform: translate(-50%, -50%);
			opacity: 0.3;
			animation: grow 2s infinite ease-in-out;
		}
		
		&-number {
			font-size: 8rem;
			line-height: 0.4;
			color: $primary-color-tint;
		}
	}
}

.poke-classic {
	.poke-image {
		&-img {
			image-rendering: pixelated;
		}
	}
}

.animate-section {
	&-enter-active,
	&-leave-active {
		transition: 0.4s ease-in-out;
	}

	&-enter,
	&-leave-to {
		opacity: 0;
	}
	
	&-enter {
		.poke-final-score-number {
			transform: translateY(-30px);
		}
	}
	
	&-leave-active {
		transform: translateX(-30%);
	}

	&-enter-active {
		transition-delay: 0.1s;
		position: absolute;
		top: 0;
		left: 50%;
		transform: translateX(-50%);
	}
}

.animate-options {
	&-enter-active {
		transition: 0.4s ease-in-out;
		
		@for $i from 4 through 8 {
			&:nth-child(#{$i}) {
				transition-delay: 0.2s * ($i - 4);
			}
		}
	}
	
	&-enter {
		transform: rotateX(-45deg);
		transform-origin: top center;
		opacity: 0;
	}
	
	&-leave-active {
		position: absolute;
		z-index: -1;
		transition: 0.8s ease-in-out;
		
		// @for $i from 0 through 3 {
		// 	$child: $i + 1;
		// 	&:nth-child(#{$child}) {
		// 		top: ($options-height + $border-width / 2) * $i;
		// 	}
		// }
		
		&[data-index="0"] {
			top: 0;
		}
		
		&[data-index="1"] {
			top: ($options-height + $border-width / 2);
		}
		
		&[data-index="2"] {
			top: ($options-height + $border-width / 2) * 2;
		}
		
		&[data-index="3"] {
			top: ($options-height + $border-width / 2) * 3;
		}
	}
	
	&-leave-to {
		opacity: 0;
	}
}

@keyframes grow {
	0%, 100% { transform: translate(-50%, -50%) scale(1) }
	50% { transform: translate(-50%, -50%) scale(0.6) }
}
View Compiled
console.clear();

const pkmnTotal = 802;
const url = `https://pokeapi.co/api/v2/pokemon/?limit=${pkmnTotal}`;
const optionAmount = 4;
let pokemonData = [];
const prettyNames = {
	'nidoran-f': 'nidoran♀',
	'nidoran-m': 'nidoran♂',
	'mr-mime': 'mr. mime',
	'deoxys-normal': 'deoxys',
	'wormadam-plant': 'wormadam',
	'mime-jr': 'mime jr.',
	'giratina-altered': 'giratina',
	'shaymin-land': 'shaymin',
	'basculin-red-striped': 'basculin',
	'darmanitan-standard': 'darmanitan',
	'tornadus-incarnate': 'tornadus',
	'thundurus-incarnate': 'thundurus',
	'landorus-incarnate': 'landorus',
	'keldeo-ordinary': 'keldeo',
	'meloetta-aria': 'meloetta',
	'meowstic-male': 'meowstic',
	'aegislash-shield': 'aegislash',
	'pumpkaboo-average': 'pumpkaboo',
	'gourgeist-average': 'gourgeist',
	'oricorio-baile': 'oricorio',
	'lycanroc-midday': 'lycanroc',
	'wishiwashi-solo': 'wishiwashi',
	'type-null': 'type: null',
	'minior-red-meteor': 'minior',
	'mimikyu-disguised': 'mimikyu',
	'tapu-koko': 'tapu koko',
	'tapu-lele': 'tapu lele',
	'tapu-bulu': 'tapu bulu',
	'tapu-fini': 'tapu fini'
}

const app = new Vue({
	el: '#app',
	filters: {
		prettifyName(name) {
			return prettyNames[name] || name;
		}
	},
	data() {
		return {
			pokemon: [],
			pkmnAmount: null,
			score: 0,
			question: 0,
			questionAmount: 10,
			answer: {},
			selected: {},
			options: [],
			isPlaying: false,
			isDone: false,
			isChecked: false,
			trainerHovered: null
		}
	},
	computed: {
		image() {
			let url = 'https://raw.githubusercontent.com/tiffachoo/pokesprites/master/pokemon/'
			let imageUrl = `${url}${this.classic ? 'redblue' : 'sunmoon'}/`
			let number = this.answer.url.match(/\/(\d+)/)[1];
			return `${imageUrl}${number}.png`
		},
		classic() {
			return this.pkmnAmount <= 151
		}
	},
	mounted() {
		let pokeList = localStorage.getItem('pokeList');
		
		if (pokeList) {
			pokemonData = JSON.parse(pokeList);
		} else {
			this.getData()
				.then(res => {
					pokemonData = res.results
					localStorage.setItem('pokeList', JSON.stringify(res.results));
			});
		}
	},
	methods: {
		getData() {
			return fetch(url)
				.then(res => res.json())
				.catch(err => console.log('errrr'));
		},
		startGame(val) {
			this.question = 0;
			this.score = 0;
			this.isPlaying = true;
			this.pokemon = [...pokemonData];
			
			this.pkmnAmount = val || pkmnTotal;
			
			this.getNextQuestion();
		},
		getNextQuestion() {
			this.question += 1;
			this.resetAnswer();
			
			if (this.question <= this.questionAmount) {
				let removed = '';
				for (let i = 1; i <= optionAmount; i++) {
					removed = this.pokemon.splice(this.getRandomPokemon(i), 1)[0];
					if (i === 1) {
						this.answer = removed;
					} else {
						this.options.push(removed);
					}
				}

				let pos = Math.floor(Math.random() * optionAmount);
				this.options.splice(pos, 0, this.answer);
			} else {
				this.isPlaying = false;
				this.isDone = true;
				this.resetAnswer();
			}
		},
		selectAnswer(ans, index) {
			if (!this.isChecked) {
				this.$set(this.selected, 'name', ans);
				this.$set(this.selected, 'index', index);
			}
		},
		checkAnswer() {
			this.isChecked = true;
			
			if (this.selected.name === this.answer.name) {
				this.score += 10;
			}
		},
		getRandomPokemon(index) {
			const diff = (this.question - 1) * 4 + index;
			return Math.floor(Math.random() * (this.pkmnAmount + 1 - diff));
		},
		resetAnswer() {
			this.options = [];
			this.selected = {};
			this.answer = {};
			this.isChecked = false;
		},
		restartGame() {
			this.isDone = false;
		},
		trainerHover(val) {
			this.trainerHovered = val;
		}
	}
})
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js