- var cardText = [{name:"Don Joel",position:"Web Developer",email:"donjoel@example.com",phone:"216-362-0665",address:"2699 Glenwood Avenue",city:"Brook Park, OH 44142"},{name:"Joe Schmoe",position:"Graphic Designer",email:"joeschmoe@example.com",phone:"407-712-8549",address:"469 Grand Avenue",city:"Winter Park, FL 32789"},{name:"Clint Westwood",position:"Customer Support",email:"clintwestwood@example.com",phone:"865-217-3165",address:"2212 Brown Avenue",city:"Hartford, TN 37753"},{name:"Ann Thrax",position:"Project Manager",email:"annthrax@example.com",phone:"808-293-4613",address:"3801 Stratford Drive",city:"Laie, HI 96762"}];
// Note: No real addresses and phone numbers were used here. They all came from fakenamegenerator.com.

mixin cardText(name,position,email,phone,address,city)
	.contents
		h2
			strong="ABC"
			="Design"
		h3=name
		=position
		br
		br
		span="✉️"
		=email
		br
		span="📞"
		=phone
		br
		br
		=address
		br
		=city
		
mixin cityOnly(city)
	.contents
		.city=city
	
main
	.cards
		- var cards = 36;
		- while (cards--) {
			//- choose card contents
			- var c = cards % cardText.length;

			a(href="#").stack
				.card.top
					+cardText(cardText[c].name,cardText[c].position,cardText[c].email,cardText[c].phone,cardText[c].address,cardText[c].city)
				.card.mid
					+cityOnly(cardText[c].city)
				.card.bottom
					+cityOnly(cardText[c].city)
				.card.shadow
		- }
View Compiled
*,
*:before,
*:after {
	border: 0;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
:root {
	font-size: 18px;
	--cardW: 14em;
	--cardH: 8em;
	--cardZInc: 2em;
}
body {
	background: hsl(0,0%,100%);
	font: 1em "Open Sans", sans-serif;
	height: 100vh;
	overflow-x: hidden;
}
main {
	display: block;
	margin: auto;
	position: relative;
	height: calc(var(--cardH) * 36 - 1.5em);
}
/* Grid */
.cards,
.stack {
	transform-style: preserve-3d;
}
.cards {
	--scroll: 0;
	display: grid;
	grid-template: repeat(36, var(--cardH)) / var(--cardW);
	grid-gap: 1.5em;
	position: fixed;
	top: 50%;
	left: 50%;
	transform: translateX(-50%) rotateX(45deg) rotateZ(45deg) translateY(var(--scroll));
	transform-origin: 50% 0;
}
.stack {
	display: block;
	position: relative;
}
.contents {
	color: #000;
	font-size: 0.5em;
	line-height: 1.25;
	-webkit-font-smoothing: antialiased;
}
.stack:nth-of-type(even) .contents {
	color: #fff;
}
.contents h2 {
	color: #aff;
	font-size: 2em;
	font-weight: 400;
	margin-bottom: 0.25em;
}
.contents span {
	margin-right: 0.75em;
}
.city {
	margin-top: 11.75em;
}
/* Card backgrounds */
.stack:nth-of-type(4n + 2) .card {
	background-position: 0 100%;
}
.stack:nth-of-type(4n + 3) .card {
	background-position: 100% 0;
}
.stack:nth-of-type(4n + 4) .card {
	background-position: 100% 100%;
}
.stack:nth-child(8n + 5) .card,
.stack:nth-child(8n + 6) .card,
.stack:nth-child(8n + 7) .card,
.stack:nth-child(8n + 8) .card {
	filter: hue-rotate(90deg);
	-webkit-filter: hue-rotate(90deg);
}
.stack:nth-child(12n + 9) .card,
.stack:nth-child(12n + 10) .card,
.stack:nth-child(12n + 11) .card,
.stack:nth-child(12n + 12) .card {
	filter: hue-rotate(180deg);
	-webkit-filter: hue-rotate(180deg);
}
/* Card hover */
.stack:hover .top,
.stack:focus .top {
	transform: translateZ(calc(var(--cardZInc) * 3));
}
.stack:hover .mid,
.stack:focus .mid {
	transform: translateZ(calc(var(--cardZInc) * 2));
}
.stack:hover .bottom,
.stack:focus .bottom {
	transform: translateZ(var(--cardZInc));
}
.stack:hover .shadow,
.stack:focus .shadow {
	filter: blur(6px);
	-webkit-filter: blur(6px);
	opacity: 0.4;
}
/* Other card styles */
.card {
	background-image: url("https://assets.codepen.io/416221/business-card-backgrounds.png");
	background-size: 200% 200%;
	box-shadow: -1px -1px 0 hsla(0,0%,0%,0.2) inset;
	color: #000;
	padding: 0.75em;
	position: absolute;
	transition: all 0.3s;
	width: 100%;
	height: 100%;
}
.top {
	transform: translateZ(3px);
	z-index: 3;
}
.mid {
	transform: translateZ(2px);
	z-index: 2;
}
.bottom {
	transform: translateZ(1px);
	z-index: 1;
}
.shadow {
	background: hsl(0,0%,0%);
	filter: blur(0);
	-webkit-filter: blur(0);
	opacity: 0.7;
}

/* Alter grid at breakpoints */
@media screen and (min-width: 361px) {
	main {
		height: calc(var(--cardH) * 18 - 1.5em);
	}
	.cards {
		grid-template: repeat(18, var(--cardH)) / repeat(2, var(--cardW));
	}
}
@media screen and (min-width: 641px) {
	main {
		height: calc(var(--cardH) * 12 - 1.5em);
	}
	.cards {
		grid-template: repeat(12, var(--cardH)) / repeat(3, var(--cardW));
	}
}
@media screen and (min-width: 961px) {
	main {
		height: calc(var(--cardH) * 9 - 1.5em);
	}
	.cards {
		grid-template: repeat(9, var(--cardH)) / repeat(4, var(--cardW));
	}
}
@media screen and (min-width: 1281px) {
	main {
		height: calc(var(--cardH) * 8 - 1.5em);
	}
	.cards {
		grid-template: repeat(8, var(--cardH)) / repeat(5, var(--cardW));
	}
}

/* Dark mode optimization */
@media screen and (prefers-color-scheme: dark) {
	body {
		background: hsl(0,0%,20%);
	}
}
window.addEventListener("resize",scrollGrid);
window.addEventListener("scroll",scrollGrid);

function scrollGrid() {
	const bodyHeight = document.body.offsetHeight;
	const mainHeight = document.querySelector("main").offsetHeight;
	const cards = document.querySelector(".cards");
	const transY = (window.pageYOffset / (mainHeight - bodyHeight)) * -100;
	
	cards.style.setProperty("--scroll",transY + "%");
}
scrollGrid();

External CSS

  1. https://fonts.googleapis.com/css?family=Open+Sans:400,700

External JavaScript

This Pen doesn't use any external JavaScript resources.