|
| 1 | +--- |
| 2 | +target: apps/landing homepage |
| 3 | +total_score: 26 |
| 4 | +p0_count: 0 |
| 5 | +p1_count: 4 |
| 6 | +timestamp: 2026-08-17T07-46-31Z |
| 7 | +slug: apps-landing-src-pages-index-tsx |
| 8 | +--- |
| 9 | +# Critique — apps/landing homepage |
| 10 | + |
| 11 | +Register: brand (marketing surface). Target: `apps/landing/src/pages/index.tsx`, verified live at `http://localhost:4000` at 1440×900. |
| 12 | + |
| 13 | +## Design Health Score |
| 14 | + |
| 15 | +| # | Heuristic | Score | Key Issue | |
| 16 | +|---|-----------|-------|-----------| |
| 17 | +| 1 | Visibility of System Status | 3 | Hover/focus states are solid; nothing signals scroll position on a 12-section page | |
| 18 | +| 2 | Match System / Real World | 3 | Developer vocabulary is right; "0 limits" doesn't parse as a phrase | |
| 19 | +| 3 | User Control and Freedom | 3 | Feature cards look interactive but aren't; no route from the bento to `/features/*` | |
| 20 | +| 4 | Consistency and Standards | 2 | Four labels for one signup action; two identical bento grids; type tokens bypassed 33× | |
| 21 | +| 5 | Error Prevention | 3 | n/a for a marketing page | |
| 22 | +| 6 | Recognition Rather Than Recall | 2 | Feature grid is a dead end; only 5 of ~16 `/vs` pages surfaced | |
| 23 | +| 7 | Flexibility and Efficiency | 3 | Nav dropdown is well built and keyboard-accessible | |
| 24 | +| 8 | Aesthetic and Minimalist Design | 2 | Whole viewports render empty; the same proof point repeats up to 5× | |
| 25 | +| 9 | Error Recovery | 3 | n/a | |
| 26 | +| 10 | Help and Documentation | 2 | FAQ structured data is emitted with no visible FAQ on the page | |
| 27 | +| **Total** | | **26/40** | **Acceptable** | |
| 28 | + |
| 29 | +## Anti-Patterns Verdict |
| 30 | + |
| 31 | +**Deterministic scan**: `detect.mjs` over `index.tsx` + `src/components` returns `[]`. No gradient text, no side-stripes, no over-rounded cards, no sketchy SVG, no glassmorphism. |
| 32 | + |
| 33 | +**LLM assessment**: this does not read as AI-generated. Funnel Display at extrabold with a real tuned scale, a committed black/neutral/white palette, and the MCP artifact all read as authored. Two patterns from the ban list survive: |
| 34 | + |
| 35 | +- **Identical card grids, twice.** The features bento and the testimonials bento are the same composition: black featured card spanning 2 cols / 2 rows top-left, white bordered cards filling the rest, `lg:auto-rows-[17rem]`. `index.tsx:573` and `index.tsx:977` are structurally the same block. |
| 36 | +- **Section eyebrows, reintroduced.** `SectionHeader.tsx` carries a comment explaining why the tracked-caps eyebrow was deleted. `index.tsx:544-549` reintroduces it inline as `/ Setup`, `/ Pricing`, `/ Ownership`. |
| 37 | + |
| 38 | +## Overall Impression |
| 39 | + |
| 40 | +The typography and the restraint are genuinely good. The problem is that the page is running at 60% density: it is 12 sections long, several of which render as empty screens, and it shows the product exactly once (the MCP exchange). Everything else is a claim in large type. The single biggest opportunity is filling the empty containers with the artifacts that already exist in this codebase. |
| 41 | + |
| 42 | +## What's Working |
| 43 | + |
| 44 | +- **The MCP section** (`index.tsx:763-803`). The `Artifact` + `AgentExchange` pairing is the one place a visitor sees Plunk do something: a real prompt, the real tool names, the real confirmation gate. It is the strongest thing on the page and it sets the bar the rest doesn't meet. |
| 45 | +- **The type system** in `globals.css:79-110`. A ~1.28-ratio scale with a deliberate 14px-for-chrome-only carve-out, documented in the file. The reasoning is better than most design systems ship with. |
| 46 | +- **Motion hygiene.** `MotionConfig reducedMotion="user"` in `_app.tsx:57` plus the marquee's `prefers-reduced-motion` block. Not an afterthought. |
| 47 | +- **The competitor list** (`index.tsx:469-504`). Editorial, tactile, hover translate. Good instinct. |
| 48 | + |
| 49 | +## Priority Issues |
| 50 | + |
| 51 | +### [P1] The feature bento is five dead-end cards wrapped around empty space |
| 52 | + |
| 53 | +`index.tsx:573-616`. The featured Workflow Automation card is ~500px tall with an icon at the top, a title at the bottom, and nothing between. The five small cards each carry ~120px of the same void. None of them link anywhere. |
| 54 | + |
| 55 | +Meanwhile `src/components/sections/` holds `WorkflowChain`, `FilterBuilder`, `StepSequence`, `SpecList`, `StatBand` — all code-drawn artifacts, all already shipping on `/features/workflows`, `/features/segments`, `/features/inbound-email`, `/features/email-editor`. The homepage imports none of them. |
| 56 | + |
| 57 | +**Why it matters**: the visitor is told six things and shown none of them, on the surface where the burden of proof is highest. And the six cards that most obviously want to be clicked lead nowhere, so the only route to a feature page is the nav dropdown. |
| 58 | + |
| 59 | +**Fix**: put `WorkflowChain` inside the featured card. Make each card a `Link` to its `/features/*` page with the same hover treatment as the competitor rows. Drop `justify-between` where there's no content to justify. |
| 60 | + |
| 61 | +**Suggested command**: `$impeccable craft` the feature bento, then `$impeccable layout`. |
| 62 | + |
| 63 | +### [P1] Entire viewports render empty |
| 64 | + |
| 65 | +At 1440×900, scrolling the page produces at least two screens with essentially nothing on them: |
| 66 | + |
| 67 | +- The pricing section (`index.tsx:883`) uses `sm:py-56` (224px top and bottom) plus `mt-24` on the price block. Between the section header and `$0.001` there is a full 785px screen of nothing. |
| 68 | +- The unified-contacts section (`index.tsx:682`) puts `my-10` around an 80px SVG arrow inside a `max-w-3xl` column, producing a near-blank screen holding one 40×80 arrowhead. |
| 69 | + |
| 70 | +**Why it matters**: on a long page, an empty screen reads as "the page broke" or "this is over", not as breathing room. Generous spacing is a brand permission; a blank viewport is a bug. |
| 71 | + |
| 72 | +**Fix**: cap section padding at `sm:py-36` (already the value everywhere else — pricing is the outlier at `py-56`), and collapse the arrow gap to `my-4`. Vary the rhythm instead of maxing it. |
| 73 | + |
| 74 | +**Suggested command**: `$impeccable layout` |
| 75 | + |
| 76 | +### [P1] Hierarchy inversion: competitors and a keyword ticker are louder than Plunk |
| 77 | + |
| 78 | +The two loudest surfaces below the hero are (a) the black marquee at `text-5xl` cycling eight keywords (`index.tsx:429-460`) and (b) the competitor list where "Mailchimp" and "Customer.io" render at `lg:text-5xl` (`index.tsx:488`). |
| 79 | + |
| 80 | +**Why it matters**: at 48px, the competitor names are the biggest words on the page after the h1. A visitor skimming reads Resend, SendGrid, Mailchimp, Customer.io, Mailgun in display type before they read anything Plunk does. The marquee amplifies terms already stated in the hero stat strip directly above it, at four times the size, with zero added information. |
| 81 | + |
| 82 | +**Fix**: drop the competitor rows to `lg:text-4xl` and give "vs Plunk" real weight so the row reads as a comparison rather than a competitor billboard. Either cut the marquee or make it carry something the page doesn't already say. |
| 83 | + |
| 84 | +**Suggested command**: `$impeccable typeset` for the scale, `$impeccable distill` for the marquee. |
| 85 | + |
| 86 | +### [P1] Four labels for one action |
| 87 | + |
| 88 | +The signup CTA is called: "Get started" (nav), "Get started free" (`:331`), "Start for free" (`:961`), "Create free account" (`:1071`). All four point at `${DASHBOARD_URI}/auth/signup`. |
| 89 | + |
| 90 | +**Why it matters**: PRODUCT.md's design principle 5 is "Consistency is trust… one way to show errors, one way to show success. No creative variation in functional UI." A CTA is functional UI. Four names for one door makes the page feel written by four people. |
| 91 | + |
| 92 | +**Fix**: pick one label and use it everywhere, including the nav. "Start for free" is the most specific. |
| 93 | + |
| 94 | +**Suggested command**: `$impeccable clarify` |
| 95 | + |
| 96 | +### [P2] The same proof points are stated up to five times |
| 97 | + |
| 98 | +`$0.001` appears in the hero subtitle, the hero stat strip, the marquee, the problem section, the pricing hero, and the closing CTA. Open-source/AGPL appears in the h1, the marquee, the "Ownership" column, the open-source card trio, and the footer CTA. "5,000+ stars" appears three times. |
| 99 | + |
| 100 | +The open-source section (`index.tsx:806-879`) is the clearest casualty: three cards saying AGPL-3.0 / EU hosted / Deploy anywhere, all three already stated in the marquee and the "/ Ownership" column two sections earlier. |
| 101 | + |
| 102 | +**Why it matters**: repetition without escalation reads as thin content, not emphasis. It is also why the page needs 12 sections to say what 8 would say better. |
| 103 | + |
| 104 | +**Fix**: let each section own one claim. Cut the open-source card trio and fold self-hosting into the problem section's "Ownership" column, which already makes the argument better. |
| 105 | + |
| 106 | +**Suggested command**: `$impeccable distill` |
| 107 | + |
| 108 | +### [P2] FAQ structured data with no FAQ on the page |
| 109 | + |
| 110 | +`index.tsx:187-243` injects a `FAQPage` JSON-LD with five Q&As. None of that content is rendered. Every `/vs/*` and `/guides/*` page renders a real `FAQSection`; the homepage imports the component and never uses it. |
| 111 | + |
| 112 | +**Why it matters**: Google's structured-data policy requires FAQ content to be visible on the page; invisible FAQ markup is a manual-action risk. It is also a content gap — the five questions in that blob answer exactly what a first-time visitor asks. |
| 113 | + |
| 114 | +**Fix**: render `FAQSection` with those five entries before the closing CTA, or remove the schema. |
| 115 | + |
| 116 | +**Suggested command**: `$impeccable craft` an FAQ section. |
| 117 | + |
| 118 | +### [P2] The design system is bypassed on its own flagship page |
| 119 | + |
| 120 | +`globals.css:82` says pages "compose from these instead of hand-rolling arbitrary values." The homepage uses 33 raw Tailwind size classes against 23 token classes, plus `text-[10px]` and `text-[11px]` (`index.tsx:1024`) — both below the 12px floor the scale deliberately sets. It also carries 23 inline `style={{fontFamily: 'var(--font-display)'}}` / `var(--font-mono)` declarations, 13 of them on `h1`/`h2`/`h3` elements that `globals.css:174` already sets to the display font. |
| 121 | + |
| 122 | +**Why it matters**: the tokens exist and are well reasoned. Every raw `text-5xl` is a decision re-made by hand, which is exactly how the "flattened scale" the comments describe crept in the first time. |
| 123 | + |
| 124 | +**Fix**: add `font-display` / `font-mono` utilities in `@theme`, delete the 23 inline styles and the 13 redundant ones outright, and map the raw sizes onto the token scale. |
| 125 | + |
| 126 | +**Suggested command**: `$impeccable extract` |
| 127 | + |
| 128 | +### [P2] Every section enters the same way |
| 129 | + |
| 130 | +13 `whileInView` reveals, all `opacity 0 → 1` with `y: 16` and the same cubic-bezier. Scrolling at normal speed, sections arrive mid-fade: I captured the competitor list as five empty rows and the problem section with columns 2 and 3 blank while column 1 was legible. |
| 131 | + |
| 132 | +**Why it matters**: a uniform entrance applied to every section is the motion reflex, not motion design — and staggering `delay: i * 0.1` on content that is already fully in view means fast scrollers read blank space. |
| 133 | + |
| 134 | +**Fix**: keep the reveal for the two or three moments that earn it (the hero, the MCP artifact, the price). Let the rest render. Where a stagger stays, tighten the delay and widen the `viewport.margin` so items are done animating by the time they're centred. |
| 135 | + |
| 136 | +**Suggested command**: `$impeccable animate` |
| 137 | + |
| 138 | +## Persona Red Flags |
| 139 | + |
| 140 | +**Jordan (first-timer)**: reaches the feature bento, sees six things Plunk does, tries to click "Workflow Automation" to learn more, and nothing happens. The only path to a feature page is a nav dropdown they have to discover. Reaches the bottom having read "$0.001" five times and never seen the product's interface. |
| 141 | + |
| 142 | +**Riley (stress tester)**: scrolls at speed and catches half the page mid-fade. Notices the contact card's "89% open rate" (`index.tsx:738`) sitting under a section headed "No hyperbole" — an implausible number presented in the visual language of real data. Notices the FAQ schema with no FAQ. |
| 143 | + |
| 144 | +**Casey (mobile)**: the ticker at `text-4xl` and the competitor rows still work, but the pricing section's `py-40` plus `mt-24` means multiple thumb-flicks of blank screen between the header and the price. The five-item competitor list and the 12-section length make this a long scroll for a phone. |
| 145 | + |
| 146 | +## Minor Observations |
| 147 | + |
| 148 | +- **Logo strip** (`index.tsx:411`): `grayscale opacity-40` is faint enough that at a glance the row reads as placeholder. The five logos also differ in optical weight — `krumzi` and `Waidwissen` are wordmarks at `h-7`, `viral.app` and `SnowSEO` are patched to `h-9`. Normalize on cap-height, not pixel height, and lift the resting opacity to ~55%. |
| 149 | +- **Axis mismatch**: the contacts and pricing sections put a left-rail `SectionHeader` above centre-aligned content (`max-w-3xl mx-auto`, `text-center`). Two competing axes in one section. Pick one per section. |
| 150 | +- **`0 limits`** (`index.tsx:526`) doesn't read as English next to `< 5 min` and `AGPL-3.0`. "No limits" or "Unlimited" is the same idea and parses. |
| 151 | +- **Testimonial roles** at 10px (`index.tsx:1024`) are the smallest text on the page, on the content whose whole job is credibility. Founding Operations Manager at Resend is a name worth reading. |
| 152 | +- **`aria-hidden` on the marquee section** (`index.tsx:429`) is correct for decoration, but "5,000+ GitHub Stars" and "GDPR Compliant" are claims a screen reader user never hears. They're stated elsewhere, so this is defensible — worth a deliberate decision rather than a side effect. |
| 153 | +- Only 5 of ~16 `/vs/*` pages are surfaced. An "All comparisons" link at the end of that list is one line. |
| 154 | + |
| 155 | +## Questions to Consider |
| 156 | + |
| 157 | +- The MCP section is the only place the product appears. What would this page look like if every section had to show something rather than assert it? |
| 158 | +- Does this need 12 sections? "Simple to start / Serious at scale" already makes the pricing, setup, and ownership arguments. Three sections downstream restate them at greater length. |
| 159 | +- The competitor list, the marquee, and the pricing comparison all frame Plunk against other tools. What's the version of this page that leads with what Plunk is rather than what it replaces? |
0 commit comments