SterlingIndex Docs

Marketing Site

Architecture deep dive for the `apps/www` Next.js marketing site and its root-site deployment model.

apps/www is the standalone marketing application for SterlingIndex. It is a Next.js App Router app deployed at the root www origin and authored as a normal route tree with internal links like /product, /pricing, and /solutions/legal-recruiting.

The app is intentionally content-heavy and data-light. Most routes render static JSX or import structured page content from local modules instead of fetching CMS content or calling backend APIs at request time.

System Role

  • Primary purpose: explain SterlingIndex to new buyers, route evaluation intent toward demos, and keep existing-customer login separate from new-buyer conversion.
  • Runtime boundary: the app owns the marketing shell and public evaluation journey, but defers authenticated product access to the hosted sign-in contract exported by @sterling/auth.
  • Deployment assumption: the app owns the root path on https://www.<domain>/ and links authenticated users over to the dashboard host.
www.<domain>/
  -> src/app/layout.tsx shell
      -> NavbarRefined
      -> route page content
      -> footer + instrumentation

Route Structure

Core shell and entry points

RoutePurposePrimary file
/Homepage / category overviewapps/www/src/app/page.tsx
/productProduct mechanism and workflow storyapps/www/src/app/product/page.tsx
/pricingSales-assisted pricing router, not self-serve checkoutapps/www/src/app/pricing/page.tsx
/request-demoDemo-intent landing page and evaluation checklistapps/www/src/app/request-demo/page.tsx
/companyCompany and trust narrativeapps/www/src/app/company/page.tsx
/data-coverageMethodology and trust surfaceapps/www/src/app/data-coverage/page.tsx
/compare/leopard-solutionsCompetitor comparison pageapps/www/src/app/compare/leopard-solutions/page.tsx

Solution family

RoutePurposePrimary file
/solutionsHub page for audience selectionapps/www/src/app/solutions/page.tsx
/solutions/legal-recruitingRecruiting workflow variantapps/www/src/app/solutions/legal-recruiting/page.tsx
/solutions/law-firmsLaw-firm strategy variantapps/www/src/app/solutions/law-firms/page.tsx
/solutions/in-houseLegal ops / outside-counsel variantapps/www/src/app/solutions/in-house/page.tsx

Resource and editorial family

RoutePurposePrimary file
/resourcesResource hub that routes into evaluation contentapps/www/src/app/resources/page.tsx
/blogBlog index sourced from local post metadataapps/www/src/app/blog/page.tsx
/blog/[slug]Static article pages from local content arraysapps/www/src/app/blog/[slug]/page.tsx

Guardrails and error routes

RoutePurposePrimary file
/privacy-policyIntentionally unpublished placeholder that returns notFound() and noIndex metadataapps/www/src/app/privacy-policy/page.tsx
/404Explicit public error page that renders a standalone "Page not found" hero when the route is visited directlyapps/www/src/app/404/page.tsx

Application Shell

The global shell lives in apps/www/src/app/layout.tsx.

  • It publishes shared SITE_METADATA from apps/www/src/lib/marketing.ts.
  • It injects Google-hosted Mona Sans and Inter.
  • It applies a skip link, NavbarRefined, the shared Main wrapper, a shared footer, and a Suspense-wrapped observability client island.
  • It keeps the footer login path as a plain anchor handoff to the dashboard host by rendering FooterUtilityLink with rootBoundary.

The active navigation implementation is apps/www/src/components/sections/navbar-refined.tsx.

  • Desktop navigation is a small route map plus hover/focus dropdowns.
  • Mobile navigation is a Radix Sheet driven menu with staggered entrance animation.
  • Both desktop and mobile paths attach analytics attributes through getMarketingLinkAnalyticsAttributes.
  • Existing-customer login and demo CTA are always exposed as separate actions.

The footer implementation is apps/www/src/components/sections/footer-with-newsletter-form-categories-and-social-icons.tsx.

  • Footer categories are configured centrally in FOOTER_CATEGORIES.
  • The newsletter UI is present as a preview component, but the footer copy explicitly says signup is inactive in this build.
  • The only utility link wired in the layout is the root-boundary Sterling login link.

Content Composition Model

The marketing app uses three composition styles.

1. Direct JSX pages for flagship routes

The homepage, product, pricing, and request-demo pages are authored directly in route files:

  • apps/www/src/app/page.tsx
  • apps/www/src/app/product/page.tsx
  • apps/www/src/app/pricing/page.tsx
  • apps/www/src/app/request-demo/page.tsx

These routes compose hero, FAQ, stats, card-grid, and CTA sections directly in JSX because they carry the highest-fidelity conversion story and bespoke sequencing.

2. Data-backed page families

Two library modules hold structured content that multiple pages reuse:

  • apps/www/src/lib/solutions-trust-page-content.ts
  • apps/www/src/lib/evaluation-demand-page-content.ts

Those modules provide:

  • metadata payloads for route-level SEO,
  • section arrays for card-grid pages,
  • solution-specific content for the three audience pages,
  • blog-post metadata and article HTML,
  • compare-page proof points and pricing-table content.

This keeps many routes thin. For example:

  • apps/www/src/app/solutions/page.tsx mostly reads from solutionsHubPage,
  • the three solution detail routes all render the shared SolutionDetailPage page family,
  • apps/www/src/app/resources/page.tsx and apps/www/src/app/blog/page.tsx project arrays from evaluation-demand-page-content.ts,
  • apps/www/src/app/blog/[slug]/page.tsx statically enumerates generateStaticParams() from the local blogPosts array.

3. Shared section primitives

The section library under apps/www/src/components/sections gives the app a constrained set of reusable storytelling blocks:

  • heroes such as HeroSimpleCentered and HeroLeftAlignedWithDemo,
  • structural sections such as CardGridSection, FeaturesThreeColumn, PricingHeroMultiTier, and FAQsAccordion,
  • shell components such as NavbarRefined and the shared footer.

Low-level layout primitives under apps/www/src/components/elements keep these sections visually consistent:

  • Container sets page width,
  • SectionIntro standardizes heading blocks,
  • SurfaceCard provides the default card language,
  • Link, ButtonLink, and PlainButtonLink centralize CTA behavior and analytics,
  • Reveal and useReveal provide intersection-triggered motion.

Styling and Design System

The site styling is defined in apps/www/src/app/globals.css.

  • Tailwind CSS v4 theme tokens define the mist color ramp, background/foreground tokens, and font variables.
  • The app uses a light radial-gradient background with dark-mode counterparts instead of flat fills.
  • Global accessibility rules cover focus rings, skip-link behavior, and scroll-margin-top for section anchors.
  • Animation tokens and keyframes support reveal motion, mobile menu transitions, and Radix collapsible panels.

The app also carries apps/www/components.json, which points to a shadcn-style component setup. In practice, the active shell currently uses a narrow slice of local UI wrappers:

  • apps/www/src/components/ui/sheet.tsx
  • apps/www/src/components/ui/collapsible.tsx

Those wrappers sit on top of Radix primitives and mainly support the mobile navigation experience.

Metadata, Canonicals, and Deployment Behavior

Shared metadata behavior is centralized in apps/www/src/lib/marketing.ts.

  • buildPageMetadata() normalizes canonical paths with trailing slashes.
  • Every major public route exports metadata through that helper.
  • The privacy-policy route opts into noIndex.
  • The root layout publishes SITE_METADATA for the base shell.

Delivery behavior is configured in apps/www/next.config.ts.

  • The app deploys at the root www origin with no gateway base path.
  • trailingSlash: true keeps canonical marketing URLs normalized with a trailing slash.
  • When SENTRY_AUTH_TOKEN, SENTRY_ORG, and the project name env are present, withSentryConfig(...) uploads client source maps keyed to NEXT_PUBLIC_APP_RELEASE.
  • The Next config also publishes origin-side security headers such as HSTS, X-Frame-Options, and X-Content-Type-Options so the marketing host is ready to sit behind a Cloudflare proxy without pushing all hardening into edge-only config.
  • There is no active middleware layer in apps/www; routing is handled by App Router plus the Next.js route tree.
  • The repo includes apps/www/src/app/404/page.tsx as a normal route file; this documentation does not assume a separate not-found.tsx App Router handler exists unless one is added later.

Integrations and Cross-App Boundaries

Dashboard login contract

The most important shared-package dependency that shapes the marketing experience is @sterling/auth.

  • apps/www/src/lib/marketing.ts imports normalizeBaseUrl() and buildPlatformLoginHref().
  • packages/auth/src/index.ts still exposes dashboard handoff helpers for cross-app redirects, while marketing links directly to the platform auth entrypoint.
  • NEXT_PUBLIC_DASHBOARD_URL is the environment input; local fallback is http://localhost:8080.

That contract is why login links on the marketing site sometimes render as plain <a> tags instead of next/link: they need to leave the marketing app cleanly when sending a user into the dashboard host. The docs app now uses the same contract from its Fumadocs layout, so the public-site auth story is consistent across www and docs.

Analytics instrumentation

The app keeps analytics out of page-level JSX, but it now does initialize shared observability SDKs at the shell edge.

Observed files:

  • apps/www/src/components/analytics/marketing-instrumentation.tsx
  • apps/www/src/lib/observability-config.ts
  • apps/www/instrumentation-client.ts
  • apps/www/sentry.server.config.ts
  • apps/www/sentry.edge.config.ts
  • apps/www/src/app/global-error.tsx

Behavior:

  1. Link, ButtonLink, footer links, and navbar items still attach data-analytics-* attributes via getMarketingLinkAnalyticsAttributes().
  2. Forms still attach submit metadata via getMarketingFormAnalyticsAttributes().
  3. MarketingInstrumentation still listens for document-level click and submit events, preserving the existing window.dataLayer push and marketing:interaction custom-event bridge.
  4. The same client island now initializes PostHog for anonymous marketing analytics and emits automatic pageviews plus CTA/form events with attribution metadata.
  5. Attribution is standardized in src/lib/observability-config.ts as landing_path, referrer, and both first-touch and latest-touch UTM properties.
  6. Sentry is initialized separately for browser, server, and edge contexts so route transitions, server exceptions, and App Router global errors all report against the same www project.

Two boundary rules are intentional:

  • www stays anonymous from an observability perspective. It does not attach an authenticated user identity to Sentry or PostHog.
  • PostHog is the marketing analytics system of record, while Sentry is reserved for error and trace triage.

This pattern keeps CTA instrumentation uniform without coupling every section component to analytics code.

No live backend reads on public routes

The public marketing routes are currently static from the app's point of view.

  • No route in src/app fetches backend data.
  • Blog posts are stored in local arrays, not MDX or CMS records.
  • The request-demo route is an evaluation checklist, not a live submission flow.
  • Newsletter inputs are preview-only markers and intentionally not wired into production intake.

Runtime Environment Inputs

The checked-in public runtime contract for apps/www now includes:

  • NEXT_PUBLIC_DASHBOARD_URL for dashboard destination links
  • NEXT_PUBLIC_APP_ENV and NEXT_PUBLIC_APP_RELEASE for release correlation across Sentry and PostHog
  • NEXT_PUBLIC_SENTRY_DSN and NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE for lean frontend monitoring
  • NEXT_PUBLIC_POSTHOG_KEY and NEXT_PUBLIC_POSTHOG_HOST for anonymous marketing analytics

These values are documented in apps/www/.env.example and mirrored into the shared local/prod examples under infra/.

The repo now also documents Infisical as the source of truth for hosted and local secrets. For the marketing app, that means NEXT_PUBLIC_* values are expected to originate from the Infisical /apps/www path and sync into the Vercel project for apps/www.

Notable Flows

New-buyer evaluation path

Homepage / Product / Resources
  -> Request a Demo CTA
  -> /request-demo
  -> guided evaluation framing
  -> sales-assisted follow-up outside the marketing app

Existing-customer login path

Header or footer login CTA
  -> STERLING_LOGIN_HREF
  -> @sterling/auth preserves cross-app return-to handoffs
  -> browser leaves marketing origin

Blog publishing flow

blogPosts[] in evaluation-demand-page-content.ts
  -> /blog index card grid
  -> generateStaticParams()
  -> /blog/[slug]
  -> dangerouslySetInnerHTML article render

Verification Surfaces

The marketing app includes lightweight guardrail tests in apps/www/tests.

  • auth-intent-links.test.mjs verifies cross-origin login handoff behavior and login CTA wiring.
  • marketing-guardrails.test.mjs verifies metadata helper usage, accessibility shell hooks, analytics instrumentation, and the unpublished privacy-policy route.

Build and local scripts are declared in apps/www/package.json.

  • pnpm --dir apps/www dev
  • pnpm --dir apps/www build
  • pnpm --dir apps/www test
  • pnpm --dir apps/www lint

Because most content is static and local, the app's build behavior is predictable: route generation is mostly a function of source files and local content modules rather than remote dependencies.

Key Files

  • apps/www/next.config.ts
  • apps/www/src/app/layout.tsx
  • apps/www/src/app/page.tsx
  • apps/www/src/app/pricing/page.tsx
  • apps/www/src/app/request-demo/page.tsx
  • apps/www/src/app/blog/[slug]/page.tsx
  • apps/www/src/components/sections/navbar-refined.tsx
  • apps/www/src/components/sections/footer-with-newsletter-form-categories-and-social-icons.tsx
  • apps/www/src/components/page-family/solution-detail-page.tsx
  • apps/www/src/components/analytics/marketing-instrumentation.tsx
  • apps/www/src/lib/observability-config.ts
  • apps/www/instrumentation-client.ts
  • apps/www/sentry.server.config.ts
  • apps/www/sentry.edge.config.ts
  • apps/www/src/app/global-error.tsx
  • apps/www/src/lib/marketing.ts
  • apps/www/src/lib/evaluation-demand-page-content.ts
  • apps/www/src/lib/solutions-trust-page-content.ts
  • packages/auth/src/index.ts

On this page