feat: landing page, unified navbar, and interactive product mocks - #60
Conversation
Replaces the `/` -> `/inkwell` redirect with a full marketing landing surface. Adds a site-wide unified `SiteNav`, four interactive mock surfaces previewing each product tab, and paper-style research sections (Abstract, Method pipeline, Corpus stats, References). Why - The homepage was a one-line redirect; first impression was the empty Inkwell shell. We needed an actual entry point that explains the project, shows the four surfaces, and links into the deck and repo. - The Inkwell / Blots / Game / Quill tabs had their own ad-hoc header. Consolidating into a single sticky nav at the root layout removes duplication and gives consistent navigation everywhere. Highlights - Landing page: hero with 3D cursor-tilt mascot, paper-style Abstract, 5-stage clickable Method pipeline, Corpus stat cards, References + one-click BibTeX copy, 5-axis stylometric fingerprint, team grid. - Four interactive mocks pulled from the live tab routes: Inkwell scatter with Classical / Modern / Colour + algo / llm / crowd / blend toggle (110 procedural blots), Blotting Game in all three modes (Smudge -> Swatch, Smudge -> Wheel with 36-slice clickable wheel, Twin Smudges) with streak + score, Quill diff with live heuristic hue readout + 5-target rewrite picker. - Mascot3D: rotateX / rotateY cursor tracking, parallax drop-shadow, screen-blend radial sheen, scale on hover. - Unified SiteNav mounted in `app/layout.tsx`. Brand + tab list + GitHub link + contextual CTA. Active route via `usePathname` with `aria-current="page"` and an ink-bleed underline. Deletes `app/(tabs)/layout.tsx` and `components/tabs-nav.tsx`. - Layout flex chain: body `flex min-h-dvh flex-col` -> SiteNav (sticky) + main `flex flex-1 flex-col`. `CanvasShell` drops the magic `h-[calc(100dvh-57px)]` for `flex flex-1`. Hydration fix - Math.cos / sin chains in `inkwell-mock` and `game-mock` produced subtly different floats under Node-V8 (SSR) vs browser-V8 -- React warned on mismatching `left` / `top` / SVG `d` attributes. Quantize every DOM-bound float at generation time via `toFixed(2-3)`. Next.js 16 compliance - `<Image priority />` is deprecated in v16. Migrated to `loading="eager" fetchPriority="high"` per the installed docs. Security - `pnpm audit` flagged two moderate transitive CVEs (already fixed on main): GHSA-67mh-4wv8-2f99 (esbuild via drizzle-kit) and GHSA-qx2v-qp2m-jg93 (postcss via Next). Branch keeps the workspace overrides clean; `pnpm audit` is clean. Cleanup applied in this change - Deleted orphan `public/inkling-mascot.png` (418KB, zero refs). - Removed unused `@keyframes inklings-bleed-in` and `.inklings-bleed-in` utility. - Stripped redundant `useMemo` wrappers in `game-mock.tsx`; collapsed `wheel*` and `swatch*` into a single `guess*` pair. - Exported `ArrowIcon` from `site-nav` instead of duplicating it in `page.tsx`. Verified: pnpm typecheck + biome check clean, /, /inkwell, /blots, /game, /quill all return 200, no hydration warnings.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Code reviewFound 1 issue:
Inklings/components/landing/quill-mock.tsx Lines 71 to 86 in bd22545 🤖 Generated with Claude Code If this code review was useful, please react with 👍. Otherwise, react with 👎. |
There was a problem hiding this comment.
Pull request overview
This PR replaces the / redirect with a full landing page that introduces the project and previews the four product “surfaces”, while also unifying navigation into a single sticky SiteNav mounted at the root layout.
Changes:
- Add a new landing page at
/with multiple sections and interactive mock previews (Inkwell/Game/Quill) to route users into the app. - Replace the per-tab header (
TabsNav+(tabs)/layout) with a site-wideSiteNavinapp/layout.tsx. - Update layout/CSS to use a flex-based height chain and add landing-specific animation utilities.
Reviewed changes
Copilot reviewed 16 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Rewrites project overview, quickstart, and structure docs for the new landing + features. |
| components/tabs-nav.tsx | Deletes old tabs navigation component (replaced by SiteNav). |
| components/site-nav.tsx | Adds unified sticky navbar, GitHub link, CTA, and shared icons/constants. |
| components/landing/research.tsx | Adds landing sections (Abstract/Method/Corpus/References). |
| components/landing/quill-mock.tsx | Adds interactive Quill mock (live hue + target nudges). |
| components/landing/profile-bars.tsx | Adds stylometric “fingerprint” bar component. |
| components/landing/mascot-3d.tsx | Adds interactive 3D tilt mascot component. |
| components/landing/inkwell-mock.tsx | Adds interactive Inkwell mock (layout + hue source toggles). |
| components/landing/game-mock.tsx | Adds interactive Blotting Game mock (swatch/wheel/twin modes). |
| components/landing/book-card.tsx | Adds landing book preview cards. |
| components/landing/blot.tsx | Adds reusable blot SVG component for landing visuals. |
| components/canvas/canvas-shell.tsx | Switches to flex-1 height strategy to remove header-height coupling. |
| app/page.tsx | Replaces redirect with the full landing page implementation. |
| app/layout.tsx | Mounts SiteNav, updates metadata/viewport, and establishes flex layout chain. |
| app/globals.css | Adds landing animation keyframes/utilities. |
| app/(tabs)/layout.tsx | Deletes old tab layout wrapper/header (nav moved to root layout). |
Comments suppressed due to low confidence (1)
components/landing/research.tsx:174
- There’s a duplicated arrow SVG here while
ArrowIconis already exported fromcomponents/site-nav.tsx. Reusing the shared icon would reduce duplication and keep the icon style consistent across the site.
function Arrow() {
return (
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| canvas/ — deck.gl canvas shell | ||
| landing/ — blot, profile-bars, book-card, inkwell-mock | ||
| ui/ — shadcn primitives | ||
| tabs-nav.tsx · providers.tsx |
| cp .env.example .env.local # fill DATABASE_URL, OPENAI_API_KEY, ANTHROPIC_API_KEY | ||
| pnpm db:push # apply Drizzle schema to Neon | ||
| pnpm seed:tracer # seed a starter corpus | ||
| pnpm dev # http://localhost:3000 → /inkwell |
| <nav className="flex items-center gap-1"> | ||
| <ul className="hidden items-center gap-1 md:flex"> | ||
| {TABS.map((t) => ( | ||
| <li key={t.href}> | ||
| <TabLink href={t.href} label={t.label} active={isActive(pathname, t.href)} /> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| <GithubLink /> | ||
| {onHome && <CtaButton href="/inkwell" label="Enter the Inkwell" />} | ||
| </nav> |
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import { GITHUB_URL } from "@/components/site-nav"; | ||
|
|
| const siteUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "Inklings", | ||
| description: "A canvas of authors, in shape and in hue.", | ||
| metadataBase: new URL(siteUrl), | ||
| title: { | ||
| default: "Inklings — What color is Shakespeare?", |
| export default function LandingPage() { | ||
| return ( | ||
| <div className="relative overflow-x-hidden"> | ||
| <Hero /> | ||
| <Abstract /> | ||
| <Problem /> | ||
| <CoreIdea /> |
Landing page, unified navbar, and interactive product mocks
Summary
Previously
/was a one-lineredirect("/inkwell"). This PR ships an actual landing surface that explains the project, previews all four product surfaces with interactive mocks, and routes visitors into them. It also consolidates the per-tab headers into one site-wideSiteNav, swaps the Next 16-deprecated<Image priority />forloading="eager" fetchPriority="high", and fixes a hydration mismatch caused by SSR-vs-browser float drift.User flow
Landing page anatomy
flowchart TB Body["app/layout.tsx<br/>body: flex min-h-dvh flex-col"] Body --> Nav["SiteNav<br/>sticky · usePathname"] Body --> Main["main: flex flex-1 flex-col"] Main --> Page["app/page.tsx · LandingPage RSC"] Main --> Tabs["app/(tabs)/* routes"] Page --> Sections subgraph Sections [Landing sections] direction TB Hero["Hero + Mascot3D"] Abs["Abstract"] Prob["Problem"] Core["Core idea"] Meth["Method pipeline"] Surf["4 Surface previews"] Profile["Stylometric fingerprint"] Corp["Corpus stats"] Refs["References + BibTeX"] Team["Team"] Footer["Footer"] Hero --> Abs --> Prob --> Core --> Meth --> Surf --> Profile --> Corp --> Refs --> Team --> Footer end Surf --> Mocks subgraph Mocks [Interactive mocks · client components] IM["InkwellMock"] GM["GameMock"] QM["QuillMock"] BK["BookCard grid"] endHighlights
Landing surface (
app/page.tsx)Interactive mocks (
components/landing/*-mock.tsx)InkwellMockGameMockΔ°+ points.QuillMockMascot3DrotateX/rotateYcursor tracking,scale(1.05)on hover, parallaxdrop-shadow, radial sheen viamix-blend-mode: screen.Unified
SiteNav(components/site-nav.tsx)The Inkwell,The Blots,The Blotting Game,The Quill) + GitHub icon + contextual Enter the Inkwell CTA on/only.usePathnamewitharia-current="page"and anink-bleedunderline.GITHUB_URL,GithubIcon,ArrowIconso consumers share one source of truth.Layout flex chain
body: flex min-h-dvh flex-col→SiteNav (sticky)+main: flex flex-1 flex-col.CanvasShellpreviously usedh-[calc(100dvh-57px)](coupled to assumed header height). Nowflex flex-1— no magic numbers.Hydration safety
Math.cos/Math.sinchains ininkwell-mock(blot positions) andgame-mock(color-wheel SVGdattributes) produced subtly different floats under Node-V8 vs browser-V8. React fired hydration warnings on the trailing digits. Fixed by quantizing all DOM-bound floats with.toFixed(2-3)at generation time.Next.js 16 compliance
<Image priority />is deprecated in v16 (see installednode_modules/next/dist/docs/01-app/03-api-reference/02-components/image.md).loading="eager" fetchPriority="high"on LCP candidates (SiteNavbrand,Mascot3Dhero).Mascot3Dprop renamedpriority→eagerto mirror the new semantics.Security
pnpm-workspace.yamloverrides resolve two moderate transitive CVEs:esbuild(<= 0.24.2) → forced>= 0.25.0drizzle-kit > @esbuild-kit/esm-loader > @esbuild-kit/core-utils > esbuildpostcss(< 8.5.10) → forced>= 8.5.10next > postcsspnpm auditis clean.Cleanup applied in this PR
public/inkling-mascot.png(418 KB, zero refs after the-no-backgroundmigration).@keyframes inklings-bleed-inand.inklings-bleed-inutility.useMemowrappers ingame-mock.tsx(4 wrappers around primitive expressions) into a singleguess*pair.ArrowSVG inpage.tsxwith the sharedArrowIconexported fromsite-nav.tsx.File-by-file
New
app/page.tsx(was 6 lines of redirect; now full landing)app/{favicon.ico,icon.png,apple-icon.png,opengraph-image.png,twitter-image.png}— Next 16 metadata-based asset routespublic/inkling-mascot-no-background.png— transparent mascotbrand/*.png— GitHub avatar + social preview source assetscomponents/site-nav.tsxcomponents/landing/blot.tsxcomponents/landing/book-card.tsxcomponents/landing/inkwell-mock.tsxcomponents/landing/game-mock.tsxcomponents/landing/quill-mock.tsxcomponents/landing/mascot-3d.tsxcomponents/landing/profile-bars.tsxcomponents/landing/research.tsxModified
app/layout.tsx— mountsSiteNav, body flex column, full metadata + viewport exportsapp/globals.css— landing keyframes (inklings-float,inklings-drift,inklings-bar-fill,inklings-spin-slow,inklings-pulse-soft)components/canvas/canvas-shell.tsx—h-[calc(100dvh-57px)]→flex flex-1README.md— project overview, problem, core idea, features matrixDeleted
app/(tabs)/layout.tsx— header moved to root layoutcomponents/tabs-nav.tsx— replaced bySiteNavpublic/inkling-mascot.png— orphanTest plan
pnpm typecheckcleanpnpm check(biome) cleanpnpm auditclean/,/inkwell,/blots,/game,/quillreturn200/Known follow-ups (not blocking)
Mascot3DrunssetStateon everymousemove. Migrate to refs + CSS custom properties for compositor-only updates.InkwellMock,GameMock,QuillMock,MethodPipeline,References) ship in the initial JS bundle. Wrap innext/dynamic.research.tsxinto a server-only file (Abstract,CorpusStats) and a client-only file (MethodPipeline,References).min-h-0to<main>defensively for Safari flex-collapse safety.BookCard(landing) withBlotCard(app/(tabs)/blots/page.tsx) once/blotswires real 4-channel data.Generated for LMU Praktikum Informationsvisualisierung · SoSe 2026 · Inklings team.