mixin Card(id)
article.card.card_hidden(id=`card-${id}`)
.card__image.overflowFixForSafari
img(src="", alt="A nice furniture")
.card__choice
button(data-action="dislike") <svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg>
button(data-action="like") <svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg>
header.header
.header__branding Your interior design builder
nav.header__menu
ul
.header__menuItem
i.header__menuItemIcon <svg viewBox="0 0 576 512" width="100" title="home"><path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z" /></svg>
li.header__menuItemLabel Home
.header__menuItem
i#favMenuItem.header__menuItemIcon <svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg>
li.header__menuItemLabel Favorites
.header__menuItem
i.header__menuItemIcon <svg viewBox="0 0 496 512" width="100" title="compass"><path d="M225.38 233.37c-12.5 12.5-12.5 32.76 0 45.25 12.49 12.5 32.76 12.5 45.25 0 12.5-12.5 12.5-32.76 0-45.25-12.5-12.49-32.76-12.49-45.25 0zM248 8C111.03 8 0 119.03 0 256s111.03 248 248 248 248-111.03 248-248S384.97 8 248 8zm126.14 148.05L308.17 300.4a31.938 31.938 0 0 1-15.77 15.77l-144.34 65.97c-16.65 7.61-33.81-9.55-26.2-26.2l65.98-144.35a31.938 31.938 0 0 1 15.77-15.77l144.34-65.97c16.65-7.6 33.8 9.55 26.19 26.2z" /></svg>
li.header__menuItemLabel Explore
.header__menuItem
i.header__menuItemIcon <svg viewBox="0 0 512 512" width="100" title="cog"><path d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z" /></svg>
li.header__menuItemLabel Settings
.header__menuItem
i.header__menuItemIcon <svg viewBox="0 0 512 512" width="100" title="info-circle"><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z" /></svg>
li.header__menuItemLabel About
main.main
header
h1 What's your kind of interior design?
p This will help us suit our suggestions to your taste
#cards
each val in [0, 1, 2, 3, 4, 5, 6, 7]
+Card(val)
View Compiled
@import url("https://fonts.googleapis.com/css2?family=Dancing+Script&family=Playfair+Display&family=Montserrat&display=swap");
// Colors
$primary-color: #d9ccc1;
$dark-primary-color: #c5b3a3;
$secondary-color: #8c7a70;
$dark-secondary-color: #59524f;
$white-color: #f0f0f2;
$dislike-color: #b71c1c;
$like-color: $secondary-color;
// Typos
$main-typo: Montserrat, sans-serif;
$accent-typo: "Playfair Display", serif;
$special-typo: "Dancing Script", cursive;
// Settings
$menu-size: 18em;
$card-width: 15em;
$perspective: 1000px;
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
font-family: $main-typo;
@media screen and (max-width: 1200px) {
font-size: 0.8em;
}
}
h1 {
font-size: 3em;
margin-bottom: 0.25em;
}
h1,
header > h1 + * {
font-family: $accent-typo;
color: $dark-secondary-color;
}
header > h1 + * {
font-size: 1.4em;
margin-top: 0;
}
.header {
position: fixed;
float: left;
width: $menu-size;
min-height: 100%;
color: $white-color;
background-color: $dark-secondary-color;
&__menu {
& ul {
padding-left: 2em;
}
}
&__branding {
padding: 1.8em 1em 2em 1em;
font-size: 2.5em;
background-color: $secondary-color;
font-family: $special-typo;
}
&__menuItem {
margin: 3em 0;
font-size: 1.2em;
perspective: $perspective;
perspective-origin: calc(1.25em * 0.5) 50%;
&Icon {
float: left;
width: 1.25em;
height: 1.25em;
& svg {
width: 1.25em;
height: 1.25em;
fill: $white-color;
stroke: $white-color;
}
}
&Label {
display: inline-block;
list-style-type: none;
margin-left: 1em;
}
}
}
.main {
float: right;
padding: 1.25em 2.5em;
width: calc(100% - #{$menu-size});
min-height: 100%;
background-color: $primary-color;
}
#cards {
max-width: 75em;
}
.card {
display: inline-block;
position: relative;
overflow: hidden;
will-change: transform;
height: $card-width;
width: $card-width;
background-color: $dark-primary-color;
border-radius: 25px;
margin: 2em 2em 0 0;
transition: opacity 150ms ease-in-out, transform 150ms ease-in-out;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: $white-color;
transition: opacity 500ms ease-in-out;
opacity: 0;
z-index: -1;
}
&_favorited {
transform-origin: 0.5em 0.5em;
&::after {
z-index: 1;
opacity: 1;
}
}
&_hidden {
opacity: 0;
visibility: hidden;
transform: scale(0.5);
}
&__image {
width: $card-width;
height: 75%;
border-radius: 25px 25px 0 0;
& > img {
position: relative;
opacity: 0;
transition: opacity 1000ms ease-in-out;
width: 100%;
height: 100%;
}
&_loaded > img {
opacity: 1;
}
}
&__choice {
display: flex;
align-items: center;
justify-content: center;
height: 25%;
button {
font-size: 0.9em;
$size: 3em;
background-color: $dark-secondary-color;
width: $size;
height: $size;
border: none;
border-radius: calc(#{$size} / 2);
padding: 0.3em 0 0 0;
margin: 0 1em;
cursor: pointer;
& svg {
width: 1.3em;
height: 1.3em;
fill: $primary-color;
stroke: $primary-color;
}
&[data-action="dislike"]:hover,
&[data-action="dislike"]:focus {
background-color: $dislike-color;
}
&[data-action="like"]:hover,
&[data-action="like"]:focus {
background-color: $like-color;
}
}
}
}
.overflowFixForSafari {
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
}
View Compiled
const unsplashIds = [
"FV3GConVSss",
"JaXs8Tk5Iww",
"UV81E0oXXWQ",
"MNz7IGrcEl0",
"tleCJiDOri0",
"JdcJn85xD2k",
"nhWgZNV85LQ",
"UXFJ-6Zj27M",
"98WE9hWWjiQ",
"3mlg5BRUifM",
"_B9J6abAHPA",
"VOS6akpHo1k",
"f7h2nTvEknM",
"RBoGC_OJvWs",
"GliaHAJ3_5A"
];
const cards = document.querySelectorAll(".card");
const cardsContainer = document.querySelector("#cards");
const favoriteIcon = document.getElementById("favMenuItem");
function getCard(element) {
if (element.classList.contains("card")) {
return element;
} else if (element !== document.documentElement) {
return getCard(element.parentElement);
}
}
function getButton(element) {
if (element.tagName === "BUTTON") {
return element;
} else if (element !== document.documentElement)
return getButton(element.parentElement);
}
function getImg(card) {
if (!(card instanceof Element && card.classList.contains("card"))) return;
return card.querySelector("img");
}
function appear(card) {
const id = Math.floor(Math.random() * unsplashIds.length);
const cardImg = getImg(card);
const src = `https://source.unsplash.com/${unsplashIds[id]}/240x180`;
card.classList.remove("card_hidden");
cardImg.setAttribute("src", src);
}
function reset(card) {
const cardImg = getImg(card);
cardImg.setAttribute("src", "");
cardImg.parentElement.classList.remove("card__image_loaded");
card.classList.add("card_hidden");
card.classList.remove("card_favorited");
setTimeout(() => {
appear(card);
}, 400);
}
function like(card) {
const animationDuration = 1000;
const distance = getDistance(card, favoriteIcon);
card.classList.add("card_favorited");
favoriteIcon.animate([{ transform: "translateZ(700px)", offset: 0.5 }], {
duration: animationDuration,
easing: "ease-in-out"
});
const animation = card.animate(
[
{
transform: `translate(${distance.x}px, ${distance.y}px) scale(0.2) rotate(-40deg)`
}
],
{ duration: animationDuration / 2, easing: "ease-in" }
);
animation.onfinish = () => {
reset(card);
};
}
function dislike(card) {
const animation = card.animate(
[
{
transform: "scale(0.1) rotate(-130deg)",
opacity: 0
}
],
{ duration: 400, easing: "ease-in-out" }
);
animation.onfinish = () => {
reset(card);
};
}
function buttonHandler(event) {
const card = getCard(event.target);
const button = getButton(event.target);
if (!card || !button || !button.dataset) return;
if (button.dataset.action === "like") like(card);
if (button.dataset.action === "dislike") dislike(card);
}
function getDistance(elt1, elt2) {
if (!(elt1 instanceof Element && elt2 instanceof Element))
throw Error("Illegal use of the function");
const elt1Bbox = elt1.getBoundingClientRect();
const elt2Bbox = elt2.getBoundingClientRect();
return { x: elt2Bbox.x - elt1Bbox.x, y: elt2Bbox.y - elt1Bbox.y };
}
function init() {
cardsContainer.addEventListener("click", buttonHandler);
[...cards].forEach((card, index) => {
const cardImg = getImg(card);
cardImg.addEventListener("load", () => {
cardImg.parentElement.classList.add("card__image_loaded");
});
setTimeout(() => {
appear(card);
}, 200 + index * 50);
});
}
window.addEventListener("DOMContentLoaded", init);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.