Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119<script lang="ts"> import FeatureCard from "$lib/components/home/FeatureCard.svelte"; import KnotGlobe from "$lib/components/home/KnotGlobe.svelte"; import SectionHeader from "$lib/components/home/SectionHeader.svelte"; import SpindleSheets from "$lib/components/home/SpindleSheets.svelte"; import StackDemo from "$lib/components/home/StackDemo.svelte"; import Sticker from "$lib/components/home/Sticker.svelte"; import VouchChat from "$lib/components/home/VouchChat.svelte"; import { features, knotPins, stackRevisions, stackVersions, vouchChats, type Feature } from "$lib/components/home/content";
const LAYOUT = [ "[--band:5rem] [--gap:var(--spacing-xs)] [--pin:calc(var(--band)+var(--gap))] [--step:0px]", "[--card:calc(100svh-var(--pin)-var(--spacing-md))]", "md:[--band:6rem] md:[--gap:var(--spacing-md)] md:[--step:12px]", "md:[--card:min(34rem,calc(100svh-var(--band)-2*var(--gap)-var(--depth)*var(--step)))]", "md:[--pin:max(calc(var(--band)+var(--gap)),calc((var(--band)+(100svh-var(--card)-var(--depth)*var(--step))/2)/2))]" ].join(" "); const PINNED = [ "sticky h-(--card) md:h-(--card) md:self-center", "top-[calc(var(--pin)+var(--i)*var(--step))] mb-[calc((var(--depth)-var(--i))*var(--step))]", "md:w-[calc(100%-(var(--depth)-var(--i))*2rem)]" ].join(" ");
const FADE = 0.7;
let stack = $state<HTMLDivElement>(); let fades = $state<number[]>([]); let frame = 0;
function measure() { frame = 0; if (!stack) return; const cards = Array.from(stack.querySelectorAll<HTMLElement>(":scope > article")); const covered = cards.map((card) => { const { top, height } = card.getBoundingClientRect(); const pinned = parseFloat(getComputedStyle(card).top) || 0; return Math.min(Math.max(1 - (top - pinned) / height, 0), 1); }); fades = cards.map((_, i) => { const depth = covered.slice(i + 1).reduce((sum, value) => sum + value, 0); return 1 - FADE ** depth; }); }
function schedule() { if (!frame) frame = requestAnimationFrame(measure); }
$effect(() => { measure(); return () => cancelAnimationFrame(frame); });</script>
<svelte:window onscroll={schedule} onresize={schedule} />
{#snippet showcase(demo: Feature["demo"])} {#if demo === "stack"} <StackDemo revisions={stackRevisions} versions={stackVersions} /> {:else if demo === "globe"} <KnotGlobe pins={knotPins} class="scale-[0.72] sm:scale-100" /> {:else if demo === "spindle"} <SpindleSheets /> {:else} <VouchChat scripts={Object.values(vouchChats)} /> {/if}{/snippet}
<section class="px-md pt-4xl pb-5xl md:px-xl md:pt-5xl md:pb-28 {LAYOUT}" style:--depth={features.length - 1}> <div class="flow-root"> <SectionHeader title="Everything a forge should be." subtitle="And then some." class="contents" stickersClass="sticky top-0 z-10 mb-0 h-(--band) justify-center bg-background-canvas" > {#snippet stickers()} <Sticker tone="deep" rotate={-6}>What's</Sticker> <Sticker tone="soft" rotate={4} round class="z-10 -mx-1.5">in</Sticker> <Sticker tone="accent" rotate={-3}>the box</Sticker> {/snippet} </SectionHeader>
<div bind:this={stack} class="mx-auto mt-4xl -mb-[calc(var(--card)+var(--gap)+var(--depth)*var(--step))] flex max-w-page flex-col gap-[18vh] md:mt-5xl md:gap-md" > {#each features as { tone, icon, title, href, body, demo: kind }, i (title)} <FeatureCard class={PINNED} style="--i: {i}; --fade: {fades[i] ?? 0}" {tone} {icon} {title} {href} > {#each body as paragraph (paragraph)} <p>{paragraph}</p> {/each} {#snippet demo()} {@render showcase(kind)} {/snippet} </FeatureCard> {/each} </div> </div> <div class="h-[calc(var(--card)+var(--gap)+var(--depth)*var(--step))]" aria-hidden="true"></div></section>