diff --git a/www/AGENTS.md b/www/AGENTS.md index 28e5ea0e70..5a99900763 100644 --- a/www/AGENTS.md +++ b/www/AGENTS.md @@ -23,7 +23,7 @@ pnpm test:unit | `app/` | Routes. A page's body is its `page.tsx`; files only that route uses sit beside it. The home page's sections are in `app/(home)/_home/` | | `features//` | Code more than one route uses: downloads, license, announcements, analytics, docs, desktop telemetry | | `components/ui/` | shadcn primitives and `cn` | -| `components/site/` | The site's shared blocks: nav, footer, shell, FAQ, buttons, logos, JSON-LD | +| `components/site/` | The site's shared blocks: nav, footer, FAQ, buttons, logos, JSON-LD | | `content/` | MDX: `docs/`, `blog/`, `changelog/`, `announcements/` | New code starts beside the route that uses it and moves to `features/` when a second route needs it. `sitemap.ts` reads each route's data directly. Tests sit beside their code as `*.test.ts`. diff --git a/www/app/(home)/_home/home-content.ts b/www/app/(home)/_home/home-content.ts index d631e8dcbf..a9e94df552 100644 --- a/www/app/(home)/_home/home-content.ts +++ b/www/app/(home)/_home/home-content.ts @@ -190,15 +190,15 @@ export const FEATURES: { { key: "plugins", heading: "Open source, extended by plugins", - body: "Each plugin gives the agent live data from one platform. Eight ship today.", + body: "Each plugin gives the agent live data from one platform. Ten ship today.", points: [ { title: "More places to search", - body: "Google Search, YouTube, TikTok, Google Maps, Amazon, Walmart, Indeed and the open web.", + body: "Google Search, Reddit, YouTube, Instagram, TikTok, Google Maps, Amazon, Walmart, Indeed and the open web.", }, { - title: "Open by pull request", - body: "Our plugins are code in the public repo; anyone can list an MCP server in the registry.", + title: "Open to everyone", + body: "Every plugin is open source, and anyone can add their own MCP server to the registry.", }, ], action: { label: "See the plugins", href: "/plugins" }, diff --git a/www/app/(home)/_home/home-scene.tsx b/www/app/(home)/_home/home-scene.tsx index 11ee563d0f..0ecf0b9b9a 100644 --- a/www/app/(home)/_home/home-scene.tsx +++ b/www/app/(home)/_home/home-scene.tsx @@ -6,8 +6,9 @@ import heroScene from "@/app/(home)/_home/home-hero-scene.webp"; * positioned parent. * * `next/image` serves AVIF or WebP at the width each screen needs, from the - * 3296px master, and inlines a tiny blurred preview so the scene shows before - * the image arrives. + * 3296px master. Until it arrives, a gradient of the scene's own sky and ground + * stands in, so white copy stays readable from the first paint. No + * `placeholder="blur"`: its SVG blur filter is costly to paint at full width. * * `sizes` is the width the scene is drawn at, not the box width: the 1.73:1 image * covers its box, so in a tall box it renders 1.73 times the box height wide. @@ -22,6 +23,11 @@ const FILM_GRAIN = `url("data:image/svg+xml,${encodeURIComponent(GRAIN_SVG)}")`; export function HomeScene({ sizes, priority = false }: { sizes: string; priority?: boolean }) { return ( <> + {/* Averages of the scene's bands, sampled from the master: sky, haze, ground. */} +
diff --git a/www/app/(home)/announcements/[slug]/page.tsx b/www/app/(home)/announcements/[slug]/page.tsx index b967e168fa..385eb2789d 100644 --- a/www/app/(home)/announcements/[slug]/page.tsx +++ b/www/app/(home)/announcements/[slug]/page.tsx @@ -21,7 +21,7 @@ import { formatAnnouncementDate } from "../format-announcement-date"; /** * One announcement, laid out like a blog post. The header and image come from * `features/announcements/announcements-data.ts`, the text from its MDX body. - * Site design via the `/announcements/` prefix in `components/site/site-shell.tsx`. + * Site design from `app/(home)/layout.tsx`. */ const BASE_URL = "https://www.surfsense.com"; diff --git a/www/app/(home)/announcements/page.tsx b/www/app/(home)/announcements/page.tsx index ffd8acb889..b97617a783 100644 --- a/www/app/(home)/announcements/page.tsx +++ b/www/app/(home)/announcements/page.tsx @@ -9,9 +9,8 @@ import { AnnouncementCard } from "./announcement-card"; import { FeaturedAnnouncement } from "./featured-announcement"; /** - * Rendered in the site design: listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`, so the palette, column, navigation and - * footer come from the site shell. Laid out like the blog index: the newest + * Rendered in the site design: the palette, column, navigation and footer come + * from `app/(home)/layout.tsx`. Laid out like the blog index: the newest * announcement as a wide feature, the rest as cards, each linking to its page. * * Every published announcement is listed, expired or not and whatever its diff --git a/www/app/(home)/contact/page.tsx b/www/app/(home)/contact/page.tsx index 07de575509..2a877b928b 100644 --- a/www/app/(home)/contact/page.tsx +++ b/www/app/(home)/contact/page.tsx @@ -11,9 +11,7 @@ import { * * Rendered in the site design: the palette, ruled column, navigation and footer * all come from `app/(home)/layout.tsx`, and every style on this page resolves - * from `app/(home)/home.css`. It is listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`, which is what puts it inside the ruled - * column rather than on the older gradient ground. + * from `app/(home)/home.css`. * * A server component with no client JavaScript. Every element on the page is a * link. diff --git a/www/app/(home)/downloads/page.tsx b/www/app/(home)/downloads/page.tsx index 9e0b4b54c5..a836ec53d8 100644 --- a/www/app/(home)/downloads/page.tsx +++ b/www/app/(home)/downloads/page.tsx @@ -10,8 +10,7 @@ import { AllReleasesLink, OSDownloadGrid } from "./download-panels"; /** * Rendered in the site design: the palette, column, navigation and footer all * come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads - * and rounded cards. Listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`. + * and rounded cards. */ export const metadata: Metadata = { diff --git a/www/app/(home)/free/[model_slug]/page.tsx b/www/app/(home)/free/[model_slug]/page.tsx index 01b7d6a3b0..4f2b642941 100644 --- a/www/app/(home)/free/[model_slug]/page.tsx +++ b/www/app/(home)/free/[model_slug]/page.tsx @@ -41,9 +41,8 @@ import { HomeDownloadButton } from "@/features/downloads/home-download-button"; * false, so none of it survived. What replaced it says only what the desktop * app does: no account, your own key or a local model, files on your disk. * - * Rendered in the site design via the `/free/` prefix in `SITE_DESIGN_PREFIXES` - * (`components/site/site-shell.tsx`). A server component, no client JavaScript - * beyond the hero button. + * Rendered in the site design from `app/(home)/layout.tsx`. A server component, + * no client JavaScript beyond the hero button. */ interface PageProps { diff --git a/www/app/(home)/free/page.tsx b/www/app/(home)/free/page.tsx index ced54aea73..ece128478f 100644 --- a/www/app/(home)/free/page.tsx +++ b/www/app/(home)/free/page.tsx @@ -12,8 +12,7 @@ import { LinkSquare02Icon } from "@/components/ui/icons"; /** * Rendered in the site design: the palette, ruled column, navigation and footer * all come from `app/(home)/layout.tsx`; the page uses Tailwind plus the shared - * `siteText` styles. Listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`. + * `siteText` styles. * * The copy, metadata, keyword set and structured data are the ones this page * already ranks on and are left alone. diff --git a/www/app/(home)/layout.tsx b/www/app/(home)/layout.tsx index dbc17fecd4..c8a5f6611b 100644 --- a/www/app/(home)/layout.tsx +++ b/www/app/(home)/layout.tsx @@ -1,6 +1,8 @@ import { Fraunces } from "next/font/google"; import { getStarCount, STARS_HREF } from "@/components/site/github-stars"; -import { SiteShell } from "@/components/site/site-shell"; +import { SiteFooter } from "@/components/site/site-footer"; +import { SiteNav } from "@/components/site/site-nav"; +import { SiteSmoothScroll } from "@/components/site/site-smooth-scroll"; import "./home.css"; // Brand wordmark only; loaded here so only the marketing pages fetch it. @@ -12,24 +14,32 @@ const fraunces = Fraunces({ }); /** - * Layout for every marketing route. + * Layout for every marketing route: the `.ss-home` palette, the nav, the ruled + * column and the footer. * - * A server component so it can await the star count, which is cached in Next's - * Data Cache and therefore fetched at most once an hour rather than once per - * visitor. The route branching that needs `usePathname` lives in `SiteShell`, - * the client half. + * Nothing here branches on the path: a `usePathname` check once served the + * cached homepage without `.ss-home`, so it painted unstyled until hydration. * - * The fetch is cached, so it does not opt these routes into dynamic rendering: - * they stay static and are revalidated on the same hourly schedule. + * The star count is cached in Next's Data Cache, fetched at most once an hour, + * so these routes stay static. */ export default async function HomePageLayout({ children }: { children: React.ReactNode }) { const starCount = await getStarCount(); return (
- - {children} - +
+ {/* Outside the page slot, so React keeps the nav mounted across navigations. */} + + + + {/* Full width, so backgrounds can bleed; `.ss-home-page` (home.css) holds + each section to the column instead, and the footer caps its own content. */} +
+ {children} + +
+
); } diff --git a/www/app/(home)/license/activate/page.tsx b/www/app/(home)/license/activate/page.tsx index 261c6990b3..4a74d17550 100644 --- a/www/app/(home)/license/activate/page.tsx +++ b/www/app/(home)/license/activate/page.tsx @@ -10,8 +10,7 @@ import { cn } from "@/components/ui/cn"; /** * Rendered in the site design: the palette, column, navigation and footer all * come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads, - * laid out like `/pricing/individual`, which shares `GuideSteps`. Listed in - * `SITE_DESIGN_ROUTES` in `components/site/site-shell.tsx`. + * laid out like `/pricing/individual`, which shares `GuideSteps`. * * The one place the activation steps live. All three license emails link here * (`app/license/email/message.py`), as do `/license` and `/license/success`, diff --git a/www/app/(home)/license/page.tsx b/www/app/(home)/license/page.tsx index 6db4bf1efa..ee83c3f3ce 100644 --- a/www/app/(home)/license/page.tsx +++ b/www/app/(home)/license/page.tsx @@ -11,8 +11,7 @@ import { ResendForm } from "@/features/license/license-forms"; /** * Rendered in the site design: the palette, column, navigation and footer all * come from `app/(home)/layout.tsx`; the page uses the homepage's centred heads - * and the same email pill as `/downloads`. Listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`. + * and the same email pill as `/downloads`. * * This page does one thing: send a licence file back to the address it was * issued to. Without accounts, that file is the only thing a customer holds, diff --git a/www/app/(home)/plugins/page.tsx b/www/app/(home)/plugins/page.tsx index 3e1dd30192..64f3977831 100644 --- a/www/app/(home)/plugins/page.tsx +++ b/www/app/(home)/plugins/page.tsx @@ -11,8 +11,7 @@ import { registryPlugins } from "./registry-plugins"; /** * Plugins page: every plugin SurfSense lists, as a browsable gallery. * - * Rendered in the site design from `app/(home)/layout.tsx`; listed in - * `SITE_DESIGN_ROUTES` in `components/site/site-shell.tsx`. Replaces the old + * Rendered in the site design from `app/(home)/layout.tsx`. Replaces the old * `/connectors` index, which redirects here from `next.config.ts`. Other publishers' * plugins are read from the registry container when the page is rendered. */ diff --git a/www/app/(home)/pricing/individual/page.tsx b/www/app/(home)/pricing/individual/page.tsx index df92d27638..2eed596798 100644 --- a/www/app/(home)/pricing/individual/page.tsx +++ b/www/app/(home)/pricing/individual/page.tsx @@ -17,7 +17,7 @@ import { * and the Stripe Payment Link, which needs no account. Its job is that nobody * reaches Stripe unsure what they are buying. * - * Rendered in the site design (listed in `SITE_DESIGN_ROUTES`). Copy and its + * Rendered in the site design from `app/(home)/layout.tsx`. Copy and its * sources are in `individual-content.ts`. */ diff --git a/www/app/(home)/privacy/page.tsx b/www/app/(home)/privacy/page.tsx index b31744ce30..231b9a30c7 100644 --- a/www/app/(home)/privacy/page.tsx +++ b/www/app/(home)/privacy/page.tsx @@ -6,9 +6,8 @@ import { siteText } from "@/components/site/site-text"; * Privacy Policy. Plain long-form prose, laid out like a blog post: the * padded `max-w-3xl` + Tailwind `prose` wrapper is copied from * `app/(home)/blog/[slug]/page.tsx` so a legal page reads like body text - * rather than like a landing page. Listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`, which supplies the light palette, the - * navigation and the footer. + * rather than like a landing page. `app/(home)/layout.tsx` supplies the light + * palette, the navigation and the footer. * * Every factual claim here was checked against the code, not against the * previous policy or the marketing copy. The version this replaced described diff --git a/www/app/(home)/terms/page.tsx b/www/app/(home)/terms/page.tsx index dd221e2d52..3f3d0fa744 100644 --- a/www/app/(home)/terms/page.tsx +++ b/www/app/(home)/terms/page.tsx @@ -5,9 +5,8 @@ import { siteText } from "@/components/site/site-text"; /** * Terms of Service. Plain long-form prose, laid out like a blog post: the * padded `max-w-3xl` + Tailwind `prose` wrapper is copied from - * `app/(home)/blog/[slug]/page.tsx`. Listed in `SITE_DESIGN_ROUTES` in - * `components/site/site-shell.tsx`, which supplies the light palette, the - * navigation and the footer. + * `app/(home)/blog/[slug]/page.tsx`. `app/(home)/layout.tsx` supplies the light + * palette, the navigation and the footer. * * Substantive changes from the version this replaces, all because the product * changed shape: diff --git a/www/components/site/site-footer-scene.webp b/www/components/site/site-footer-scene.webp index 1c321148f5..0bef1f64d7 100644 Binary files a/www/components/site/site-footer-scene.webp and b/www/components/site/site-footer-scene.webp differ diff --git a/www/components/site/site-footer.tsx b/www/components/site/site-footer.tsx index 19283374ea..8f40ba9859 100644 --- a/www/components/site/site-footer.tsx +++ b/www/components/site/site-footer.tsx @@ -8,8 +8,7 @@ import { cn } from "@/components/ui/cn"; /** * Site footer: the site's links set straight on the page's ground, which fades * into an engraved Golden Gate Bridge that fills the open band below. - * Rendered by `app/(home)/layout.tsx` for every route under `(home)` except the - * auth pages. + * Rendered by `app/(home)/layout.tsx` for every route under `(home)`. */ const LINK_CLASS = @@ -20,21 +19,23 @@ const WORDMARK_CLASS = export function SiteFooter() { return ( -