Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="pet-pen">
	<img class="pet-image" src="https://assets.codepen.io/2345403/pixil-egg.png" alt="" />
	<div class="curtain hidden">&ensp;</div>
	<div class="overlay hidden">?</div>
</div>

<div class="bar-wrapper">
	<div class="button-bar">
		<div class="button-spacer">&ensp;</div>
		<button id="love-button">Send Love πŸ’•</button>
		<button id="curtain-button">Toggle Lights πŸ’‘</button>
		<div class="notification-center">
			<button>Notifications</button>
			<div class="notifications hidden">
				<div id="no-notifications" class="notification">
					<p>Nothing has happened...</p>
				</div>
			</div>
		</div>
	</div>
</div>
              
            
!

CSS

              
                .html {
	height: 100vh;
	width: 100vw;
	margin: 0;
	padding: 0;
}

body {
	font-family: monospace;
	background-color: hsl(80deg 50% 70%);
	/* 	background-image: linear-gradient(
		0deg,
		hsl(45deg 62% 100%) 0%,
		hsl(80deg 100% 83%) 39%,
		hsl(90deg 59% 74%) 61%,
		hsl(45deg 62% 100%) 100%
	); */
	padding: 0;
	margin: 0;
	overflow: auto;
	height: 100vh;
}

button:hover {
	opacity: 0.5;
}

button:active {
	background-color: #ababab !important;
}

/** Layout **/

.button-bar {
	/* 	position: absolute; */
	/* 	top: 20px; */
	/* 	top: 0;
	left: 20px; */
	width: 300px;

	> button {
		background-color: #fbfbfb;
		width: 100%;
		border: 0;
		font: inherit;
		padding: 0.5rem 1rem;
		border-bottom: 1px solid black;
	}
}

/** Layout Variations **/

@media (orientation: portrait) {
	body {
		display: flex;
		flex-direction: column-reverse;
		align-items: center;
		gap: 1em;
	}

	.bar-wrapper {
		flex: 0 0 150px;
		position: relative;
		height: 150px;
		width: 300px;
	}

	.button-bar {
		position: absolute;
		padding: 0;
	}
}

@media (orientation: landscape) {
	body {
		padding-left: 3em;
		display: flex;
		flex-direction: row-reverse;
		gap: 1em;
	}

	.bar-wrapper {
		flex: 0 0 150px;
		height: 150px;
		width: 300px;
	}
}

/** Button Bar **/
.button-spacer {
	background-color: #fff;
	border-bottom: 1px solid black;
}

.notification-center {
	width: 300px;
	background-color: #fff;
	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);

	> button {
		width: 100%;
		border: 0;
		font: inherit;
		background: none;
		padding: 0.5rem 1rem;
		border-bottom: 1px dotted gray;
	}
	border-radius: 0 0 1em 1em;
}

.hidden {
	display: none;
}

.notification {
	padding: 0.5rem;
	border-bottom: 1px dotted #e0e0e0;
}

.notification:last-child {
	border-bottom: none;
	border-radius: 1em;
}

/** Pet Styles **/

.heart {
	top: 0;
	left: 0;
	position: absolute;
	font-size: 1em;
	animation: burst 1000ms ease-in 0s 1 normal forwards;
	user-selection: none;
	z-index 100;
}

.pet-pen {
	background-color: white;
	margin: auto;
	width: 300px;
	border: 20px dashed black;
	position: relative;
	align-self: center;
}

.curtain {
	position: absolute;
	width: 300px;
	height: 300px;
	background-color: black;
	top: 0;
	left: 0;
}

.pet-image {
	width: 300px;
	aspect-ratio: 1;
	margin: auto;
	animation: petPulse 5s linear 2s infinite alternate;
}

/** Animations **/
.changing {
	animation: changeStage 3s ease-in-out 0s 2 alternate forwards !important;
}

.glow {
	animation: glowEffect 1500ms ease-in-out 0s infinite alternate forwards;
}

@keyframes changeStage {
	0% {
		opacity: 1;
	}
	100% {
		opacity: 0;
	}
}

@keyframes showCurtain {
	0% {
		opacity: 1;
	}
	100% {
		opacity: 0;
	}
}

@keyframes petPulse {
	0% {
		transform: scale(1);
	}
	50% {
		transform: scale(1.2);
	}
	100% {
		transform: scale(1);
	}
}

@keyframes glowEffect {
	0% {
		box-shadow: inset 0 0 0 pink, inset  0 0 0 pink;
	}
	100% {
		box-shadow: inset  -5px -5px 15px hotpink, inset  5px 5px 15px hotpink;
	}
}

@keyframes burst {
	0% {
		transform: scale(1);
		opacity: 0;
	}
	50% {
		transform: scale(10);
		opacity: 1;
	}
	100% {
		transform: scale(20);
		opacity: 0;
	}
}

              
            
!

JS

              
                // TODO
//- use "petState"
//- normalize names

// Buttons
const notificationButton = document.querySelector(
	".notification-center > button"
);
const curtainToggler = document.getElementById("curtain-button");
const loveButton = document.getElementById("love-button");
// Other elements
const notifications = document.querySelector(".notifications");
const petPen = document.querySelector(".pet-pen");
const petImage = document.querySelector(".pet-image");
const curtain = document.querySelector(".curtain");
const petOverlay = document.querySelector(".overlay");
// Images
const firstStageImage =
	"https://assets.codepen.io/2345403/pixil-little-guy.png";
const firstStageAngryImage =
	"https://assets.codepen.io/2345403/pixii-little-mad.png";

// Messages
//πŸ₯š 🐣 πŸ₯ 🐀 πŸ“ // πŸ’’ 🀬 😑
const hatchEventMessage = "🐣 The Egg Hatched!";
const firstGrowthSpurtMessage = "πŸ₯The little guy got bigger!";
const checkBackMessage = "✨ Check back later!";
const stopMessage = "πŸ’’ You made him mad!";
const brokenLightMessage = "πŸŒ‘ Good job, you broke the light";

// Pet States
// const PetStage = Object.freeze( {
// 	EGG: 'egg',
// 	HATCHING: 'hatching',
// 	BABY: 'baby'
// })
// const PetMood = Object.freeze({
// 	NEUTRAL: n,
// 	MAD: m
// });
// const petState = {
// 	mood: PetMood.NEUTRAL,
// 	stage: PetStage.EGG
// };

//utility values
let clickCount = 0; // Replace with loveLevel
let annoyanceLevel = 0;
let lightClickCount = 0;
let hatching = false;
let isTurningAngry = false;
let isLightBroken = false;

// Utility for delaying events
async function getPromise(delay) {
	return await new Promise((resolve) => setTimeout(resolve, delay));
}

// Unsorted Functions
function onNotifcationClick() {
	notificationButton.classList.remove("glow");
	if (notificationButton.getAttribute("aria-expanded") === "true") {
		notificationButton.setAttribute("aria-expanded", "false");
	} else {
		notificationButton.setAttribute("aria-expanded", "true");
	}
	notifications.classList.toggle("hidden");
}

function addNotification(message) {
	if (notifications.firstElementChild.id === "no-notifications") {
		notifications.removeChild(notifications.firstElementChild);
	}

	const newNotification = document.createElement("div");
	newNotification.classList.add("notification");
	const details = document.createElement("p");
	details.innerText = message;
	newNotification.append(details);
	notifications.appendChild(newNotification);

	notificationButton.classList.add("glow");
}

// TODO - Factor out durations
async function changeImage(url) {
	petImage.classList.add("changing");
	await getPromise(3000);
	petImage.src = url;
	await getPromise(3000);
	petImage.classList.remove("changing");
	return getPromise(0);
}

async function hatch() {
	if (!hatching) {
		hatching = true;
		changeImage(firstStageImage).then(() => {
			addNotification(hatchEventMessage);
		});
	}
}

async function showAngry() {
	if (!isTurningAngry) {
		isTurningAngry = true;
		changeImage(firstStageAngryImage).then(() => {
			addNotification(stopMessage);
		});
	}
}

async function notifyViewerOfMore() {
	setTimeout(() => {
		addNotification(checkBackMessage);
	}, 10000);
}

function sendLove() {
	if (clickCount < 10) {
		clickCount++;
		const newHeart = document.createElement("div");
		newHeart.classList.add("heart");
		newHeart.innerText = "πŸ’–";
		petPen.append(newHeart);
		setTimeout(() => {
			newHeart.remove();
		}, 2000);
	} else if (!hatching) {
		hatch();
	}
}

function toggleCurtain() {
	if (isLightBroken) {
		// Broken Light
		return;
	} else if (annoyanceLevel === 10) {
		// Gets angry
		showAngry();
		annoyanceLevel = 70;
	} else if (isTurningAngry && annoyanceLevel >= 100) {
		// Break light
		isLightBroken = true;
		curtain.classList.remove("hidden");
		addNotification(brokenLightMessage);
		notifyViewerOfMore();
		return;
	}
	if (hatching) {
		// If hatched - Increase Annoyance
		annoyanceLevel++;
	}
	curtain.classList.toggle("hidden");
}

// Listeners
notificationButton.addEventListener("click", () => {
	onNotifcationClick();
});
curtainToggler.addEventListener("click", () => toggleCurtain());
loveButton.addEventListener("click", () => sendLove());

              
            
!
999px

Console