Files
ComfyUI_frontend/apps/website/e2e
nav-tej f4088bcc63 feat(website): draft Affiliate Program landing page at /affiliates (#12002)
*PR Created by the Glary-Bot Agent*

---

Conversion-oriented English landing page that drives applications to the
Comfy Affiliate Program Google Form. Shipped behind `noindex` while copy
and assets are finalized — a follow-up PR will flip it indexable and add
the zh-CN locale.

Mirrors the type scale, brand tokens, and noindex/sitemap/robots
conventions from the [/affiliates/terms PR
(#11954)](https://github.com/Comfy-Org/ComfyUI_frontend/pull/11954) so
the two pages feel like a set. The footer's "Read the affiliate program
terms" link points at `/affiliates/terms`, which lands with #11954.

## What's in the box

Sections (in render order): hero → trust band → how-it-works →
who-we're-looking-for → program details → brand assets → FAQ → footer
CTA.

- **i18n** — Copy lives under `affiliate-landing.*` in
`src/i18n/translations.ts` (en only; zh-CN values mirror en until a
localized version lands, so non-engineers can edit copy without touching
components).
- **Component reuse audit** — Reuses `common/FAQSection`,
`common/BrandButton`, `common/SectionHeader`, `common/SectionLabel`
verbatim. Sections without a clean primitive fit (split hero, trust
band, 3-step flow, audience list, details table, brand-asset grid,
footer CTA) render inline with brand tokens rather than forking new
shared components.
- **Brand assets** — Wired via a config object in
`components/affiliates/brandAssets.ts` so 728×90 / 300×250 / 160×600 /
1200×628 banners can be dropped in later without code changes. Live
tiles for `/icons/logo.svg` and `/icons/logomark.svg`; placeholder
"Coming soon" tiles for the four banner sizes.
- **CTA** — `Apply Now` → `https://forms.gle/RS8L2ttcuGap4Q1v6`, opens
in new tab with `target="_blank"` and `rel="noopener noreferrer"`. Hero
and footer-CTA both use it.
- **Hero media slot** — Wired as a named slot (`<slot name="media">`)
with a static node-graph SVG fallback, so a video can be dropped in
later via the page without component changes.
- **FAQ structured data** — Page emits `schema.org/FAQPage` JSON-LD so
it's ready for SEO when `noindex` flips off.
- **noindex** — `<meta name="robots" content="noindex, nofollow">` via
`BaseLayout.noindex`. `/affiliates` added to `NOINDEX_PATHNAMES` in
`astro.config.ts` (excluded from sitemap for both en and zh-CN locale
prefixes via the existing `LOCALE_PREFIXES.flatMap` pattern) and to
`robots.txt Disallow` under every UA block including the explicit AI-bot
overrides (GPTBot, ClaudeBot, OAI-SearchBot, ChatGPT-User, Claude-User,
Claude-SearchBot, PerplexityBot, Google-Extended).
- **BrandButton** — Added `rel?: string` prop so external CTAs across
the site can pair `target="_blank"` with `rel="noopener noreferrer"`.
- **Routes** — Added `affiliates`, `affiliateTerms`,
`affiliateApplicationForm` to `src/config/routes.ts`.

## Component-mapping (per AGENTS.md "no speculative variants" rule)

| Section | Where it lives | Reuse decision |
|---|---|---|
| Hero (split text+media) | `components/affiliates/HeroSection.vue` |
Inline. Existing `HeroSection`s are tightly coupled to specific i18n
namespaces (`cloud.hero.*` etc.) and `legal/HeroSection` is title-only.
|
| Trust band | `components/affiliates/TrustBandSection.vue` | Inline.
`common/SocialProofBarSection` hardcodes a 12-logo client strip; not a
fit for a single text claim. |
| How-it-works | `components/affiliates/HowItWorksSection.vue` +
`common/SectionHeader` | Inline cards. Existing `StepsSection`s are
tightly bound to product-specific namespaces. |
| Who we're looking for | `components/affiliates/AudienceSection.vue` +
`common/SectionHeader` | Inline icon-bullet list.
`pricing/PricingPlanFeatureList` is shaped for plan features with
descriptions. |
| Program details | `components/affiliates/ProgramDetailsSection.vue` +
`common/SectionHeader` | Inline `<table>` (desktop) + `<dl>` (mobile).
`product/shared/ReasonSection` is a long-form "left heading + right
list" layout, not a key/value table. |
| Brand assets | `components/affiliates/BrandAssetsSection.vue` +
`common/SectionHeader` | Inline grid (no existing download-grid
primitive). |
| FAQ | `common/FAQSection.vue` (verbatim reuse) | Reuse — the component
is already generic over `headingKey`/`faqPrefix`/`faqCount`. |
| Footer CTA | `components/affiliates/FooterCtaSection.vue` +
`common/BrandButton` | Inline. `customers/WhatsNextSection` is an
arrow-CTA card, not a centered band. |

## Responsive behaviour

- Hero stacks (text above media) under `lg` (1024px).
- Program Details collapses from `<table>` (md+) to a `<dl>` definition
list (sm).
- 3-step flow collapses from 3-up grid to vertical stack at `md`.
- Brand-asset grid steps from `lg:grid-cols-4` → `sm:grid-cols-2` →
`grid-cols-1`.

## Tests

- **Vitest unit** (`src/components/affiliates/affiliateLanding.test.ts`)
— asserts i18n key structure: every section prefix exists, sections
render in order, no internal/draft keys leak, every indexed item (hero
highlights, steps, audience items, details rows, FAQs) has an English
translation.
- **Playwright e2e** (`e2e/affiliates.spec.ts`) — desktop+mobile
render/visibility under `@smoke`, plus a non-smoke `desktop
interactions` describe that exercises the Apply Now click (popup + form
URL) and FAQ accordion toggle. Mobile asserts table → definition-list
collapse and no horizontal overflow on every section. FAQPage JSON-LD
presence verified end-to-end.

Local verification: `pnpm typecheck`, `pnpm test:unit`, `pnpm exec
playwright test e2e/affiliates.spec.ts`, `pnpm knip` — all green (40
unit, 11/11 e2e).

## Out of scope (intentional follow-ups)

- zh-CN locale + `/zh-CN/affiliates` route (omitted from `robots.txt`
until the route exists; sitemap exclusion already handles it generically
via `LOCALE_PREFIXES.flatMap`).
- Flipping `noindex` off once copy and assets are finalized.
- Dropping in the four banner SVGs (728×90, 300×250, 160×600, 1200×628)
— config object is wired, just edit
`components/affiliates/brandAssets.ts`.
- Hero video (slot is wired).
- Hooking the Apply Now CTA up to a tracked URL once the affiliate
platform is live.

## Cross-PR dependency

The footer "Read the affiliate program terms" link targets
`/affiliates/terms`, which is added by #11954. Until that PR lands, the
link 404s on this branch's preview deployment; once both merge to main,
the pair is complete.

## Screenshots

Desktop (1280w) render attached.

## Screenshots

![Desktop render of /affiliates landing page
(1280w)](https://pub-1fd11710d4c8405b948c9edc4287a3f2.r2.dev/sessions/abdb5f46d61de76335da71e1dda13f2bb5d909244d19cb1c2d931634df264c81/pr-images/1778032141901-3962caf4-f3b6-45f4-9c1d-ef91e3b04441.png)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12002-feat-website-draft-Affiliate-Program-landing-page-at-affiliates-3586d73d365081cc9d78cf7ebe04747d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Glary-Bot <glary-bot@users.noreply.github.com>
Co-authored-by: Miles Ryan <thedatalife@users.noreply.github.com>
2026-06-02 21:41:47 +00:00
..