Skip to content

Add Statsig-driven marketing hero layouts with query overrides#2937

Merged
eldadfux merged 4 commits intomainfrom
feat/marketing-hero-statsig-layouts
Apr 29, 2026
Merged

Add Statsig-driven marketing hero layouts with query overrides#2937
eldadfux merged 4 commits intomainfrom
feat/marketing-hero-statsig-layouts

Conversation

@eldadfux
Copy link
Copy Markdown
Member

Introduce hero_layout experiment (0 aside, 1 bottom two-line title, 2 bottom one-line title) evaluated on the server and mirrored on the client for exposure logging. Add URL query overrides for title, subtitle, and layout, optional variation config heroLayout, and dashboard placement styling.

Tighten bottom-layout spacing, align CTA-to-screenshot gap with nav-to-banner padding, and add dev-only Statsig experiment console logging (plus ?debug_statsig for preview builds).

Made-with: Cursor

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Introduce hero_layout experiment (0 aside, 1 bottom two-line title, 2 bottom
one-line title) evaluated on the server and mirrored on the client for
exposure logging. Add URL query overrides for title, subtitle, and layout,
optional variation config heroLayout, and dashboard placement styling.

Tighten bottom-layout spacing, align CTA-to-screenshot gap with nav-to-banner
padding, and add dev-only Statsig experiment console logging (plus
?debug_statsig for preview builds).

Made-with: Cursor
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR introduces the hero_layout Statsig experiment (variants 0/1/2) with server-side evaluation, client-side exposure logging, and URL query-param overrides for QA. The implementation follows the existing best_description SSR+client bootstrap pattern and adds a placement prop to Dashboard for the new bottom-layout variants.

Two previously-flagged issues remain open: hero_title/hero_subtitle query params are reflected into SSR HTML with no production gate (phishing vector), and ?debug_statsig is accessible to any production visitor. Additionally, the client-side normalizedLayout value from Statsig is computed for exposure logging but never feeds back into the reactive layout state, meaning a bootstrap mismatch would cause the displayed variant and the logged exposure to disagree.

Confidence Score: 3/5

Not safe to merge until the phishing reflection vector for hero_title/hero_subtitle on production is addressed.

Two previously-flagged issues remain unresolved: an unguarded URL-content-reflection P0 security issue and a production-accessible debug param. The new P2 finding (layout/exposure mismatch on bootstrap divergence) does not lower the score further, but the open P0 caps confidence at 2/5 ceiling; score is set at 3 to reflect that the new code itself is otherwise well-structured.

src/lib/statsig/hero-query-overrides.ts and src/routes/(marketing)/+page.server.ts — the hero_title/hero_subtitle server-side reflection needs a non-production environment gate before merging.

Important Files Changed

Filename Overview
src/lib/statsig/hero-query-overrides.ts New module for query-param overrides; MAX_TITLE_LEN/MAX_SUBTITLE_LEN limits don't prevent the phishing reflection vector on production (flagged separately).
src/routes/(marketing)/(components)/hero.svelte Client-side Statsig layout read computes normalizedLayout but never applies it to reactive state; layout display diverges from logged exposure when bootstrap mismatches.
src/routes/(marketing)/+page.server.ts Adds parallel Statsig evaluation for hero_layout and applies query overrides server-side; building guard correctly skips URL params during prerender.
src/lib/statsig/hero-statsig.server.ts New evaluateHeroLayoutExperiment mirrors the existing best_description pattern cleanly with disableExposureLogging and normalizeHeroLayout normalization.
src/lib/components/homepage-variations/custom-hero.svelte Adds heroLayout prop and client-side query-override resolution; no Statsig exposure logging for variation pages (intentional given hardcoded configs).
src/routes/(marketing)/(components)/dashboard.svelte New placement prop cleanly splits aside vs. below styling; both variants use responsive Tailwind-JIT utilities correctly.
src/lib/statsig/client.ts StatsigBrowserClient.get return type widened to string
src/lib/statsig/constants.ts Adds STATSIG_EXPERIMENT_HERO_LAYOUT and DEFAULT_HERO_TITLE constants; straightforward additions.
src/routes/(marketing)/+page.svelte Threads heroTitle and heroLayout from server data to Hero component; minimal change.
src/routes/[variation]/+page.svelte Passes config.heroLayout ?? 0 to custom-hero; correct fallback to layout 0 when not configured.
src/lib/components/homepage-variations/variation-config.ts Adds optional heroLayout field to HomepageVariationConfig interface; properly typed and documented.
src/lib/components/AppwriteIn100Seconds.svelte Breakpoint for full-width button changed from lg to sm, consistent with other bottom-layout CTA button breakpoints.

Reviews (3): Last reviewed commit: "fixes" | Re-trigger Greptile

Comment thread src/routes/(marketing)/(components)/hero.svelte
Comment thread src/routes/(marketing)/(components)/hero.svelte
Comment on lines +1 to +10
import { normalizeHeroLayout, type HeroLayoutVariant } from './hero-layout';

/** Query overrides for marketing hero experiments (Statsig parity for local QA). */

export const HERO_LAYOUT_QUERY_KEY = 'hero_layout';
export const HERO_SUBTITLE_QUERY_KEY = 'hero_subtitle';
export const HERO_TITLE_QUERY_KEY = 'hero_title';

const MAX_SUBTITLE_LEN = 560;
const MAX_TITLE_LEN = 160;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Content injection phishing vector on production

?hero_title and ?hero_subtitle are processed server-side in +page.server.ts via resolveHeroQueryOverrides(url.searchParams, ...) and baked into SSR HTML. A shareable URL like https://appwrite.io/?hero_title=Your+account+has+been+compromised.+Reset+your+password+now will render arbitrary attacker-controlled text as the <h1> on Appwrite's branded homepage — a classic URL-reflection phishing vector. The MAX_TITLE_LEN = 160 / MAX_SUBTITLE_LEN = 560 limits still allow ample deceptive copy.

Consider gating hero_title and hero_subtitle query overrides to non-production environments (e.g. !building && ENV.PREVIEW) on the server load, while keeping hero_layout open if visual QA on production is intentional.

@eldadfux eldadfux merged commit cef1e5c into main Apr 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant