Advent of SvelteKit 2022

Christmas tree lights

Challenge

Source code

+page.svelte

<script lang="ts">
	import ChristmasTree from './ChristmasTree.svelte';
	import ChristmasLights from './ChristmasLights.svelte';
	import type { PageData } from './$types';

	export let data: PageData;
</script>

<h1>Christmas tree lights</h1>
<div>
	<ChristmasTree size={7}>
		<ChristmasLights random={data.rng} />
		<ChristmasLights random={data.rng} />
	</ChristmasTree>
</div>

<style>
	div {
		width: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		flex-direction: column;
		margin-bottom: 1rem;
	}
</style>

ChristmasLights.svelte

<script>
	export let random = Math.random;

	// Not important for the challenge, but some math fun:
	// Math.random() gives us in the range [0...1] so we multiply
	// so we can get a range [0...AMPLIFY].
	// BUT! We want our lights centered in the tree still, so we
	// subtract (AMPLIFY / 2) so that we're centered over 0
	// and our range is now [(-AMPLIFY / 2)...(AMPLIFY / 2)]
	const AMPLIFY = 15;
	const offsetY = random() * AMPLIFY - AMPLIFY / 2;
	const offsetX = random() * AMPLIFY - AMPLIFY / 2;
	// Should be the same as the length of the animation so that
	// it's evenly distributed
	const MAX_DELAY = 2;
	const twinkleDelay = random() * MAX_DELAY;
</script>

<div
	style:top="{offsetY}px"
	style:left="{offsetX}px"
	style:animation-delay="{twinkleDelay}s"
	class="twinkle"
/>

<style>
	.twinkle {
		animation: twinkle 2s ease-in-out infinite;
		border-radius: var(--radius-round);
		background: var(--yellow-1);
		width: 0.5rem;
		height: 0.5rem;
		position: relative;
		z-index: 20;
		box-shadow: var(--shadow-3);
	}
	@keyframes twinkle {
		0% {
			opacity: 0;
		}
		50% {
			opacity: 1;
		}
		100% {
			opacity: 0;
		}
	}
</style>

ChristmasTree.svelte

<script lang="ts">
	import { fade } from 'svelte/transition';
	export let size = 1;
</script>

{#if size > 1}
	<svelte:self size={size - 1}>
		<slot />
	</svelte:self>
{/if}
<div class="tree" in:fade={{ delay: size * 100 }}>
	{#each { length: size } as _}
		<div class="leaf">
			<slot />
		</div>
	{/each}
</div>

<style>
	.tree {
		display: flex;
		flex-direction: row;
		justify-content: center;
	}

	.leaf {
		position: relative;
		border-radius: var(--radius-round);
		background: var(--green-6);
		width: var(--size-9);
		height: var(--size-9);
		margin: -0.5rem;
		display: flex;
		justify-content: center;
		place-items: center;
	}
</style>