100 Days of CSS: Day 18

February 26, 2025

Elastic: I thought for a long time about the best way to achieve this effect. In the end, the solution was easier than expected.

Unsplash ↗

Project Structure

The project consists of 2 main files:

HTML Structure

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere erat at mi sagittis ultricies. Aenean cursus libero vitae odio congue volutpat.

<div class="frame">
	<div class="top"></div>
	<div class="bottom"></div>
	<div class="ellipse">
		<div class="grey"></div>
		<div class="green"></div>
	</div>
</div>

CSS Styling

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam posuere erat at mi sagittis ultricies. Aenean cursus libero vitae odio congue volutpat.

@import url(https://fonts.googleapis.com/css?family=Open+Sans:700,300);

$green: #37d493;
$grey: #444;
$speed: 5s;
$e-width: 420px;
$e-w-offset: -10px;
$e-height: 400px;

.frame {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 400px;
	height: 400px;
	margin-top: -200px;
	margin-left: -200px;
	border-radius: 2px;
	box-shadow: 4px 8px 16px 0 rgba(0, 0, 0, 0.1);
	overflow: hidden;
	font-family: "Open Sans", Helvetica, sans-serif;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

.top {
	position: absolute;
	width: 100%;
	height: 200px;
	top: 0;
	left: 0;
	background: $grey;
}

.bottom {
	@extend .top;
	top: 50%;
	background: $green;
}

.ellipse {
	position: absolute;
	z-index: 2;
	width: $e-width;
	height: $e-height;
	top: 0;
	left: $e-w-offset;
	transform-style: preserve-3d;
	animation: elastic $speed ease-in-out infinite;
	
	.grey {
		position: absolute;
		width: $e-width;
		height: $e-height;
		background: $grey;
		backface-visibility: hidden;
		border-radius: 50%;
		z-index: 2;
		transform: rotateX(0);
	}
	
	.green {
		@extend .grey;
		background: $green;
		z-index: 1;
		transform: rotateX(180deg);
	}
}


@keyframes elastic {
	0% {
		transform: rotateX(90deg);
	}
	15% {
		transform: rotateX(150deg);
	}
	20% {
		transform: rotateX(50deg);
	}
	25% {
		transform: rotateX(120deg);
	}
	30% {
		transform: rotateX(70deg);
	}
	35% {
		transform: rotateX(100deg);
	}
	40% {
		transform: rotateX(83deg);
	} 
	45% {
		transform: rotateX(93deg);
	}
	50% {
		transform: rotateX(90deg);
	}
	
	65% {
		transform: rotateX(30deg);
	}
	70% {
		transform: rotateX(130deg);
	}
	75% {
		transform: rotateX(60deg);
	}
	80% {
		transform: rotateX(110deg);
	}
	85% {
		transform: rotateX(80deg);
	}
	90% {
		transform: rotateX(97deg);
	} 
	95% {
		transform: rotateX(87deg);
	}
	100% {
		transform: rotateX(90deg);
	}
}

Reflection