<div class="container">
	<form class="form">
		<div class="signup__field">
			<label class="label">Halloween Name</label>
			<input type="text" name="halloween_name" id="halloween_name" class="name_field summon_bat">
		</div>
		<fieldset class="signup__fieldset">
			<legend>Details</legend>
			<div class="signup__field">
				<label class="label">What kind of costume are you wearing? </label>
				<div class="select-field">
					<select name="costume_type" id="costume_type" class="select-field__menu summon_bat">
						<option>Pick one</option>
						<option value="monster">Monster</option>
						<option value="spellcaster">Spellcaster</option>
						<option value="vampire">Vampire</option>
						<option value="ghost">Ghost</option>
						<option value="zombie">Zombie</option>
						<option value="pop_culture">Pop Culture Reference</option>
						<option value="other">Other</option>
					</select>
				</div>
			</div>
			<div class="signup__field">
				<div class="label">Do you prefer Tricks or Treats?</div>
				<div class="radio__container">
					<div class="radio__field">
						<input type="radio" value="tricks" name="tricksOrTreats" id="pumpkin_tricks" checked>
						<label for="pumpkin_tricks" class="pumpkin summon_bat">
							<span class="horror-input-pumpkin-stem"></span>
							<span class="horror-input-pumpkin-eye horror-input-pumpkin-eye-left"></span>
							<span class="horror-input-pumpkin-eye horror-input-pumpkin-eye-right"></span>
							<span class="horror-input-pumpkin-mouth"></span>
						</label>
						<label for="pumpkin_tricks" class="summon_bat">Tricks</label>
					</div>
					<div class="radio__field">
						<input type="radio" value="treats" name="tricksOrTreats" id="pumpkin_treats">
						<label for="pumpkin_treats" class="pumpkin summon_bat">
							<span class="horror-input-pumpkin-stem"></span>
							<span class="horror-input-pumpkin-eye horror-input-pumpkin-eye-left"></span>
							<span class="horror-input-pumpkin-eye horror-input-pumpkin-eye-right"></span>
							<span class="horror-input-pumpkin-mouth"></span>
						</label>
						<label for="pumpkin_treats" class="summon_bat">Treats</label>
					</div>
				</div>
		</fieldset>
		<div class="signup__field">
			<div class="snack__container">
				<input type="checkbox" name="bringing_snacks" id="bringing_snacks">
				<label for="bringing_snacks" class="ghost summon_bat">
					<span class="ghost-eye ghost-eye-left"></span>
					<span class="ghost-eye ghost-eye-right"></span>
					<span class="ghost-hand ghost-hand-left"></span>
					<span class="ghost-hand ghost-hand-right"></span>
					<span class="ghost-candy"></span>
				</label>
				<label for="bringing_snacks" class="summon_bat">
					I volunteer to bring a shareable snack.
				</label>
			</div>
		</div>
		<div class="signup__button">
			<button class="button summon_bat" type="button">Submit</button>
		</div>
	</form>
</div>
<div id="stone__container"></div>
<div id="darkness" class="darkness">
	<h1>Halloween Party RSVP</h1>
</div>
@use "sass:math";
@use "sass:list";

@import url("https://fonts.googleapis.com/css2?family=Creepster");

@function text-border($size, $color) {
	$return-value: ();
	
	@for $i from $size * -1 through $size {
		@for $j from $size * -1 through $size {
			$value: #{$i $j $color};
			$return-value: list.join($return-value, $value, $separator: comma);
		}
	}
	
	@return $return-value;
}

@mixin bat-generator($color, $width, $height, $wing-span, $wing-height) {
	$flap-size: math.ceil($wing-span / 4);
	$flap-angle: math.atan2($wing-height, $wing-span / 2);
	background-color: $color;
	width: $width;
	height: $height;
	border-radius: 50% / 40%;
	&::before, &::after {
		content: "";
		width: 0;
		height: 0;
		border: 0;
		border-left: solid math.ceil($width / 6) transparent;
		border-right: solid math.ceil($width / 6) transparent;
		border-bottom: solid math.ceil($height / 2) $color;
		position: absolute;
		top: 0;
	}
	&::before {
		transform-origin: bottom right;
		transform: translateY(math.ceil($height / -2)) rotate(-25deg);
	}
	&::after {
		right: 0;
		transform-origin: bottom left;
		transform: translateY(math.ceil($height / -2)) rotate(25deg);
	}
	.horror-input-bat-left-wing,
	.horror-input-bat-right-wing {
		width: 0;
		height: 0;
		border: 0;
		border-left: solid math.ceil($wing-span / 2) transparent;
		border-right: solid math.ceil($wing-span / 2) transparent;
		border-bottom: solid $wing-height $color;
		position: absolute;
		top: calc(50% - #{$wing-height});
		&::after {
			content: "";
			background-image:
				radial-gradient(
					circle at math.ceil($flap-size / 2) math.ceil($flap-size / 2),
					transparent 0 math.ceil($flap-size / 2),
					$color math.ceil($flap-size / 2) $flap-size
				);
			background-size: $flap-size math.ceil($flap-size / 2);
			width: $wing-span;
			height: math.ceil($flap-size / 2);
			position: absolute;
			top: $wing-height;
			left: math.ceil($wing-span / -2);
			transform-origin: top center;
		}
	}
	.horror-input-bat-left-wing {
		right: calc(100% - 4px);
		transform-origin: bottom right;
		transform: rotate(-25deg);
		animation: horror-input-bat-left-wing-flap 256ms ease-out infinite;
		&::after {
			transform: skewX($flap-angle * -1);
		}
	}
	.horror-input-bat-right-wing {
		left: calc(100% - 4px);
		transform-origin: bottom left;
		transform: rotate(25deg);
		animation: horror-input-bat-right-wing-flap 256ms ease-out infinite;
		&::after {
			transform: skewX($flap-angle);
		}
	}
	@keyframes horror-input-bat-left-wing-flap {
		0% {
			transform: rotate(-25deg);
		}
		50% {
			transform: rotate(25deg);
		}
		100% {
			transform: rotate(-25deg);
		}
	}
	@keyframes horror-input-bat-right-wing-flap {
		0% {
			transform: rotate(25deg);
		}
		50% {
			transform: rotate(-25deg);
		}
		100% {
			transform: rotate(25deg);
		}
	}
}

@mixin pumpkin-generator($color, $width, $height) {
	$stem-width: math.ceil($height / 8);
	$stem-height: math.ceil($height / 3);
	$eye-width: math.ceil($width / 4);
	$eye-height: math.ceil($height / 7);
	$mouth-width: math.ceil($width / 2);
	$mouth-height: math.ceil($height / 3);
	background-color: $color;
	width: $width;
	height: $height;
	display: inline-flex;
	position: relative;
	border-radius: 30% / 50%;
	&::after {
		content: "";
		background-image:
			radial-gradient(
				circle at 25% 25%,
				transparent 0% 45%,
				rgba(#000000, 0.55) 90%
			),
			radial-gradient(
				circle at 25% 25%,
				rgba(#ffffff, 0.3) 0% 5%,
				transparent 25% 90%
			);
		width: 100%;
		height: 100%;
		position: absolute;
		top: 0;
		left: 0;
		border-radius: 30% / 50%;
	}
	.horror-input-pumpkin-stem {
		width: 0;
		height: 0;
		border: 0;
		border-left: solid $stem-width transparent;
		border-right: solid $stem-width transparent;
		border-top: solid $stem-height #00ab03;
		position: absolute;
		bottom: 100%;
		left: calc(50% - #{$stem-width});
	}
	.horror-input-pumpkin-eye {
		background-color: #000000;
		width: $eye-width;
		height: $eye-height;
		position: absolute;
		top: 25%;
		border-radius: 50% 50% 0% 0% / 100% 100% 0% 0%;
	}
	.horror-input-pumpkin-eye-left {
		left: 30%;
	}
	.horror-input-pumpkin-eye-right {
		left: 70%;
	}
	.horror-input-pumpkin-mouth {
		background-color: #000000;
		width: $mouth-width;
		height: $mouth-height;
		position: absolute;
		bottom: 10%;
		right: 15%;
		border-radius: 0% 0% 50% 50% / 0% 0% 100% 100%;
		&::before, &::after {
			content: "";
			background-color: $color;
			width: math.ceil($mouth-width / 4);
			height: math.ceil($mouth-height / 3);
			position: absolute;
		}
		&::before {
			top: 0;
			left: math.ceil($mouth-width / 8);
		}
		&::after {
			bottom: 0;
			right: math.ceil($mouth-width / 8);
		}
	}
}

@mixin ghost-generator($color, $width, $height) {
	$color-dark: darken($color, 25%);
	background-image: radial-gradient(
		circle at 25% 25%,
		$color 0% 25%,
		$color-dark 60%
	);
	width: $width;
	height: $height;
	display: inline-flex;
	position: relative;
	vertical-align: middle;
	border-radius: 50% 50% 0% 0%;
	&::after {
		content: "";
		background-image: radial-gradient(
			circle at math.ceil($width / 8) 0,
			$color-dark 0px (math.ceil($width / 8) - 1px),
			transparent math.ceil($width / 8)
		);
		background-size: math.ceil($width / 4) math.ceil($width / 8);
		width: $width;
		height: math.ceil($width / 8);
		position: absolute;
		top: 100%;
	}
	.ghost-eye {
		background-color: #000000;
		width: math.ceil($height / 8);
		height: math.ceil($height / 8);
		position: absolute;
		top: math.ceil($height / 2);
		border-radius: 50%;
	}
	.ghost-eye-left {
		left: math.ceil($height / 8);
	}
	.ghost-eye-right {
		right: math.ceil($height / 8);
	}
	.ghost-hand {
		width: math.ceil($width / 3);
		height: math.ceil($width / 4);
		position: absolute;
		top: 55%;
		transition: transform 64ms ease-out;
	}
	.ghost-hand-left {
		background-image: linear-gradient(10deg, $color, $color-dark);
		right: 100%;
		border-radius: 50% 0% 0% 50%;
		transform-origin: top right;
		transform: skewY(10deg);
	}
	.ghost-hand-right {
		background-image: linear-gradient(-10deg, $color, $color-dark);
		left: 100%;
		border-radius: 0% 50% 50% 0%;
		transform-origin: top left;
		transform: skewY(-10deg);
	}
}

html, body {
	font-family: "Creepster", cursive;
	width: 100%;
	height: 100%;
	cursor: none;
}

body {
	font-size: 16px;
	background-image: url("https://images.unsplash.com/photo-1541508186468-098826081946?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzNTM5NTY0NA&ixlib=rb-1.2.1&q=85");
	background-size: cover;
	background-position: center;
	background-attachment: fixed;
	display: grid;
	place-items: center;
	overflow: auto;
}

.container {
	padding: 32px;
}

.form {
	color: #efefef;
	background-color: rgba(#2f2f2f, 0.5);
	backdrop-filter: blur(5px);
	padding: 32px;
	border-radius: 16px;
	box-shadow: 8px 8px 16px rgba(#000000, 0.5);
	input, select , button {
		font-family: inherit;
		font-size: inherit;
		background-color: #ffffff;
		padding: 8px;
		border: 0;
		outline: 0;
		border-radius: 4px;
	}
	fieldset {
		padding: 16px;
		padding-bottom: 0;
		margin-bottom: 16px;
	}
	button {
		$button-color: #2873d4;
		color: #ffffff;
		background-color: $button-color;
		width: 100%;
		box-sizing: border-box;
		transition: background-color 64ms ease-out;
		&:hover {
			background-color: darken($button-color, 10%);
		}
		&:active {
			background-color: darken($button-color, 20%);
		}
	}
}

.signup__field {
	margin-bottom: 16px;
	display: flex;
	flex-direction: column;
}

.label {
	margin-bottom: 8px;
}

.radio__container {
	display: flex;
	justify-content: flex-start;
}

.radio__field {
	margin-right: 8px;
	&:last-child {
		margin-right: 0px;
	}
}

.signup__button {
	margin-top: 32px;
}

.signup__fieldset {
	margin-top: 32px;
}

#pumpkin_tricks,
#pumpkin_treats {
	opacity: 0;
	pointer-events: none;
	position: absolute;
	&:not(:checked) + .pumpkin {
		filter: grayscale(100%);
		& > .horror-input-pumpkin-eye,
		& > .horror-input-pumpkin-mouth {
			transform: translateX(100%) scale(0);
		}
	}
}

.pumpkin {
	@include pumpkin-generator(#fc8803, 24px, 16px);
	vertical-align: middle;
	transition: filter 64ms ease-out;
	& > .horror-input-pumpkin-eye,
	& > .horror-input-pumpkin-mouth {
		transition: transform 64ms ease-out;
	}
}

.ghost {
	@include ghost-generator(#ffffff, 12px, 16px);
	$color-1: #ff1f57;
	$color-2: #ffffff;
	margin-right: 8px;
	.ghost-candy {
		$candy-size: 10px;
		background-image: radial-gradient(
			circle at 50% 25%,
			rgba(#ffffff, 0.5) 0% 25%,
			rgba(#000000, 0.5) 60%
		), linear-gradient(
			$color-1 0% 25%,
			$color-2 25% 75%,
			$color-1 75% 100%
		);
		background-size: 100% 100%, math.ceil($candy-size / 3) math.ceil($candy-size / 3);
		width: $candy-size;
		height: $candy-size;
		position: absolute;
		top: 50%;
		left: calc(50% - #{$candy-size / 2});
		border-radius: 50%;
		opacity: 0;
		transform: translate(100%, -100%) rotate(-45deg);
		transition: opacity 64ms ease-out, transform 64ms ease-out;
		&::before,
		&::after {
			content: "";
			width: 0px;
			height: 0px;
			display: block;
			position: absolute;
			top: calc(50% - #{math.ceil($candy-size / 3)});
			border-top: solid math.ceil($candy-size / 3) transparent;
			border-bottom: solid math.ceil($candy-size / 3) transparent;
		}
		&::before {
			border-left: solid math.ceil($candy-size / 3) $color-1;
			right: 100%;
		}
		&::after {
			border-right: solid math.ceil($candy-size / 3) $color-1;
			left: 100%;
		}
	}
}

#bringing_snacks {
	opacity: 0;
	pointer-events: none;
	position: absolute;
	&:checked + .ghost {
		.ghost-hand-left {
			transform: rotateY(180deg) skewY(10deg);
		}
		.ghost-hand-right {
			transform: rotateY(180deg) skewY(-10deg);
		}
		.ghost-candy {
			opacity: 1;
			transform: rotate(-45deg);
		}
	}
}

.horror-input-bat {
	--scale: 1;
	@include bat-generator(#000000, 24px, 16px, 72px, 8px);
	position: absolute;
	transform: scale(var(--scale));
	pointer-events: none;
}

.stone__image {
	--rotate-speed: 0ms;
	position: absolute;
	transform-origin: center center;
	animation: stone-spin var(--rotate-speed) linear infinite;
}

@keyframes stone-spin {
	0% {
		transform: rotate(0deg) translate(-50%, -50%);
	}
	99.99999% {
		transform: rotate(360deg) translate(-50%, -50%);
	}
	100% {
		transform: rotate(0deg) translate(-50%, -50%);
	}
}

#stone__container {
	width: 100%;
	height: 100%;
	position: fixed;
	top: 0;
	left: 0;
	pointer-events: none;
}

.darkness {
	--x: 0px;
	--y: 0px;
	--level: 0;
	--low: 0px;
	--high: 0px;
	width: 100%;
	height: 100%;
	background-image: radial-gradient(
		circle at var(--x) var(--y),
		rgba(0, 0, 0, var(--level)) 0px var(--low),
		rgb(0, 0, 0) var(--high)
	);
	position: fixed;
	top: 0;
	left: 0;
	pointer-events: none;
	& > h1 {
		font-size: 3rem;
		text-align: center;
		color: #bf1f1f;
		text-shadow: text-border(3px, #000000);
	}
}
View Compiled
////////////////////////////////////////////////////////////////////////////////
//                       THE DARKNESS COVERS THE SCREEN                       //
////////////////////////////////////////////////////////////////////////////////

enum StoneLocation {
	Left,
	Right,
}

const bats: HTMLElement = document.querySelectorAll(".summon_bat");

let isActive: boolean = false;
let light: any = {
	low: 0,
	high: 0,
	x: 0,
	y: 0,
	level: 1,
};

let lightTween: any = gsap.to(light, {});

function random(min: number, max: number, flt: number): number {
	flt = flt === undefined ? 0 : flt;
  flt = 10 ** flt;
  min *= flt;
  max *= flt;
  
  return Math.round(Math.random() * (max - min) + min) / flt;
}

function renderLight(): void {
	gsap.set("#darkness", {
		"--x": `${light.x}px`,
		"--y": `${light.y}px`,
		"--level": `${light.level}`,
		"--low": `${light.low}px`,
		"--high": `${light.high}px`,
	});
}

function createBat(scale: number): HTMLElement {
	const bat: HTMLElement = document.createElement("DIV");
	const batLeftWing: HTMLElement = document.createElement("DIV");
	const batRightWing: HTMLElement = document.createElement("DIV");
	
	bat.classList.add("horror-input-bat");
	batLeftWing.classList.add("horror-input-bat-left-wing");
	batRightWing.classList.add("horror-input-bat-right-wing");
	
	bat.appendChild(batLeftWing);
	bat.appendChild(batRightWing);
	
	bat.style.setProperty("--scale", scale);
	
	return bat;
}

function createStone(): void {
	setTimeout((): void => {
		const stoneContainer: HTMLElement = document.getElementById("stone__container");
		const stone: HTMLElement = document.createElement("IMG");
		
		stone.src = "https://assets.codepen.io/430361/scart-select-stone.png";
		stone.classList.add("stone__image");
		stone.style.setProperty("--rotate-speed", `${random(2000, 4000)}ms`);
		
		stoneContainer.appendChild(stone);
		
		const location: StoneLocation = random(0, 1);
		
		let x1: number = 0;
		let x2: number = window.innerWidth;
		
		if (location === StoneLocation.Right) {
			x1 = window.innerWidth;
			x2 = 0;
		}
		
		let y1: number = random(0, window.innerHeight);
		let y2: number = random(0, window.innerHeight);
		
		gsap.fromTo(stone, {
			top: y1,
			left: x1,
		}, {
			top: y2,
			left: x2,
			duration: random(10, 20),
			onComplete(): void {
				for (let target of this._targets) {
					stoneContainer.removeChild(target);
				}
			},
		});
		
		createStone();
	}, random(2000, 4000));
}

document.body.addEventListener("mouseover", (evt: MouseEvent): void => {
	isActive = true;
	
	lightTween.kill();
	lightTween = gsap.to(light, {
		low: 20,
		high: 100,
		level: 0.5,
		ease: "elastic.out(0.5, 0.1)",
		duration: 1,
		onUpdate() {
			renderLight();
		},
	});
});

document.body.addEventListener("mouseout", (evt: MouseEvent): void => {
	isActive = false;
	
	lightTween.kill();
	lightTween = gsap.to(light, {
		level: 1,
		ease: "power4.inOut",
		duration: 0.3,
		onUpdate() {
			renderLight();
		},
	});
});

document.body.addEventListener("mousemove", (evt: MouseEvent): void => {
	if (!isActive) {
		return;
	}
	
	light.x = evt.pageX - window.scrollX;
	light.y = evt.pageY - window.scrollY;
	
	renderLight();
});

for (let bat of bats) {
	bat.addEventListener("click", (evt: MouseEvent): void => {
		const x: number = evt.pageX - window.scrollX;
		const y: number = evt.pageY - window.scrollY;
		const count: number = random(2, 4);
		const scale: number = random(0.3, 1, 2);
		
		for (let i = 0; i < count; i++) {
			const batElm: HTMLElement = createBat(scale);
			const gotoX: number = x + random(-100, 100);
			const gotoY: number = y + random(-100, 100);
			const duration: number = random(3, 6, 2);
			
			document.body.appendChild(batElm);
			
			batElm.style.top = `${y}px`;
			batElm.style.left = `${x}px`;
			
			gsap.to(batElm, {
				top: `${gotoY}px`,
				left: `${gotoX}px`,
				"--scale": 0,
				duration: duration,
				ease: "power4.out",
				onComplete(): void {
					for (let target of this._targets) {
						document.body.removeChild(target);
					}
				}
			});
		}
	});
}

renderLight();
createStone();
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.co/[email protected]/dist/gsap.min.js