From 75752da19f7ae46afea8e6afdb86484197a4e4e5 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Fri, 2 Oct 2026 00:01:47 +0100 Subject: [PATCH] web: float the marketing nav and add a halftone footer Signed-off-by: oppiliappan --- web/package.json | 1 + web/pnpm-lock.yaml | 24 + web/src/lib/components/home/Halftone.svelte | 37 ++ web/src/lib/components/home/halftone.ts | 22 + web/src/lib/components/shell/Footer.svelte | 461 ++++---------------- web/src/lib/components/shell/Topbar.svelte | 9 +- web/src/routes/+layout.svelte | 4 +- 7 files changed, 186 insertions(+), 372 deletions(-) create mode 100644 web/src/lib/components/home/Halftone.svelte create mode 100644 web/src/lib/components/home/halftone.ts diff --git a/web/package.json b/web/package.json index 14d7eaed6..b39d1ec11 100644 --- a/web/package.json +++ b/web/package.json @@ -85,6 +85,7 @@ "markdown-it": "^14.3.0", "markdown-it-anchor": "^9.2.1", "markdown-it-emoji": "^3.1.0", + "runed": "0.37.1", "sanitize-html": "^2.17.6", "tailwind-merge": "^3.6.0", "tailwind-variants": "^3.2.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 2a7f448b7..5e0973c7f 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -69,6 +69,9 @@ importers: markdown-it-emoji: specifier: ^3.1.0 version: 3.1.0 + runed: + specifier: 0.37.1 + version: 0.37.1(@sveltejs/kit@2.69.3(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.9(@typescript-eslint/types@8.64.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))(svelte@5.56.9(@typescript-eslint/types@8.64.0))(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))(svelte@5.56.9(@typescript-eslint/types@8.64.0)) sanitize-html: specifier: ^2.17.6 version: 2.17.6 @@ -3119,6 +3122,18 @@ packages: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} + runed@0.37.1: + resolution: {integrity: sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA==} + peerDependencies: + '@sveltejs/kit': ^2.21.0 + svelte: ^5.7.0 + zod: ^4.1.0 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + zod: + optional: true + sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -6326,6 +6341,15 @@ snapshots: run-applescript@7.1.0: {} + runed@0.37.1(@sveltejs/kit@2.69.3(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.9(@typescript-eslint/types@8.64.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))(svelte@5.56.9(@typescript-eslint/types@8.64.0))(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))(svelte@5.56.9(@typescript-eslint/types@8.64.0)): + dependencies: + dequal: 2.0.3 + esm-env: 1.2.2 + lz-string: 1.5.0 + svelte: 5.56.9(@typescript-eslint/types@8.64.0) + optionalDependencies: + '@sveltejs/kit': 2.69.3(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.9(@typescript-eslint/types@8.64.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)))(svelte@5.56.9(@typescript-eslint/types@8.64.0))(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)) + sade@1.8.1: dependencies: mri: 1.2.0 diff --git a/web/src/lib/components/home/Halftone.svelte b/web/src/lib/components/home/Halftone.svelte new file mode 100644 index 000000000..5fda9bf06 --- /dev/null +++ b/web/src/lib/components/home/Halftone.svelte @@ -0,0 +1,37 @@ + + + diff --git a/web/src/lib/components/home/halftone.ts b/web/src/lib/components/home/halftone.ts new file mode 100644 index 000000000..c50afad81 --- /dev/null +++ b/web/src/lib/components/home/halftone.ts @@ -0,0 +1,22 @@ +export const PITCH = 18; + +const ROW = PITCH / 2; + +const dot = (x: number, y: number, r: number) => + `M${x - r} ${y}a${r} ${r} 0 1 0 ${2 * r} 0a${r} ${r} 0 1 0 ${-2 * r} 0z`; + +export const halftone = ( + width: number, + height: number, + radius: (x: number, y: number) => number +): string => { + const out: string[] = []; + for (let row = 0; row * ROW <= height; row++) { + const y = row * ROW; + for (let x = (row % 2) * ROW; x <= width; x += PITCH) { + const r = radius(x, y); + if (r > 0) out.push(dot(x, y, +r.toFixed(2))); + } + } + return out.join(""); +}; diff --git a/web/src/lib/components/shell/Footer.svelte b/web/src/lib/components/shell/Footer.svelte index 5f0546278..8a42f7bab 100644 --- a/web/src/lib/components/shell/Footer.svelte +++ b/web/src/lib/components/shell/Footer.svelte @@ -1,40 +1,20 @@ -{#snippet footerIcon(icon: FooterIcon)} - {#if icon === "terms"} - - - - - - - - {:else if icon === "privacy"} - - - - {:else if icon === "blog"} - - - - - {:else if icon === "docs"} - - - - {:else if icon === "source"} - - - - - {:else if icon === "brand"} - - - - - - - - {:else if icon === "discord"} - {@render children()} + {:else} + {@render children()} - - - {:else if icon === "irc"} - - - - - - - {:else if icon === "bluesky"} - - {:else if icon === "twitter"} - - - - {:else if icon === "email"} - - - - - {:else if icon === "security"} - - - - {/if} {/snippet} @@ -338,116 +120,63 @@ tangled - © 2026 Tangled Labs Oy. + {COPYRIGHT} - {#each minimalFooterLinks as link (link.href)} - {#if link.internal} - {link.minimalLabel ?? link.label} - {:else} - {link.minimalLabel ?? link.label} - {/if} + {#each minimalLinks as link (link.href)} + {#snippet label()}{link.minimalLabel ?? link.label}{/snippet} + {@render anchor( + link, + "text-foreground-muted hover:text-foreground-default hover:underline", + label + )} {/each} {:else} -
-
-
-
- - -
-
- - - -
+
+ +
+
+ + + +

+ {COPYRIGHT} All rights reserved. +

+
-
- {#each footerSections as section (section.title)} -
-
{section.title}
- {#each section.links as link (link.href)} - {#if link.internal} - {link.label} - {:else} - {link.label} - {/if} - {/each} -
+
-
-
+ {/each} +
{/if} diff --git a/web/src/lib/components/shell/Topbar.svelte b/web/src/lib/components/shell/Topbar.svelte index 671410059..47d8a3c09 100644 --- a/web/src/lib/components/shell/Topbar.svelte +++ b/web/src/lib/components/shell/Topbar.svelte @@ -23,7 +23,10 @@ }, variants: { variant: { - marketing: { actions: "gap-3.5" }, + marketing: { + nav: "mx-auto h-14 max-w-page rounded-2xl border border-border-default bg-background-default/80 px-sm shadow-small backdrop-blur-md", + actions: "gap-xs" + }, app: { nav: "border-b border-border-default bg-background-default", actions: "gap-xs" @@ -103,14 +106,12 @@ {#if variant === "marketing"} - or - diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 179a0814b..6b06ea427 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -97,13 +97,13 @@ class={isBareShell ? "flex min-h-screen flex-col bg-background-default text-foreground-default dark:bg-background-canvas" : isMarketingShell - ? "flex min-h-screen flex-col bg-linear-to-b from-background-canvas to-background-default text-foreground-default" + ? "relative flex min-h-screen flex-col overflow-clip bg-background-canvas text-foreground-default" : "flex min-h-screen flex-col bg-background-canvas text-foreground-default"} > {#if !isBareShell && !isWelcomeShell}