Skip to content

Commit 1824278

Browse files
feat: add testimonials section to homepage (#70)
Add "What engineers are saying" section between Features and Self-Hosting on the homepage. - Pull-quote editorial design with bold highlight phrases and full quotes - Testimonials data as list in PageController for easy growth - Author roles (Principal Engineer, Senior Software Engineer) and X handles - Scroll-triggered staggered fade-up animation - Full-width bg-secondary band for visual section separation - Subtle email CTA for submitting new testimonials Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6813fa1 commit 1824278

File tree

5 files changed

+286
-2
lines changed

5 files changed

+286
-2
lines changed

assets/js/app.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,22 @@ if (featuresGrid) {
156156
observer.observe(featuresGrid)
157157
}
158158

159+
// Home page: testimonials scroll-triggered reveal
160+
const testimonialsGrid = document.getElementById("testimonials-grid")
161+
if (testimonialsGrid) {
162+
const observer = new IntersectionObserver((entries) => {
163+
entries.forEach(entry => {
164+
if (entry.isIntersecting) {
165+
testimonialsGrid.querySelectorAll(".feature-card").forEach((card, i) => {
166+
setTimeout(() => card.classList.add("revealed"), i * 150)
167+
})
168+
observer.unobserve(entry.target)
169+
}
170+
})
171+
}, { threshold: 0.1 })
172+
observer.observe(testimonialsGrid)
173+
}
174+
159175
// Integrations page: tab switching
160176
document.querySelectorAll(".integration-tab").forEach(tab => {
161177
tab.addEventListener("click", () => {
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Testimonials Section Implementation Plan
2+
3+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4+
5+
**Goal:** Add a "What engineers are saying" testimonials section to the homepage between the Features section and the Self-Hosting card.
6+
7+
**Architecture:** Testimonials are defined as a module attribute list in `page_controller.ex` (same pattern as `@features`), passed as an assign, and rendered via a `:for` comprehension in the template. Pure Tailwind styling, no custom CSS.
8+
9+
**Tech Stack:** Phoenix, HEEx templates, Tailwind CSS with `--crit-*` CSS variables
10+
11+
---
12+
13+
### Task 1: Add testimonials data to PageController
14+
15+
**Files:**
16+
- Modify: `lib/crit_web/controllers/page_controller.ex:140-168`
17+
18+
- [ ] **Step 1: Add `@testimonials` module attribute after `@feature_order` (line 140)**
19+
20+
Add the following after line 140 (`@feature_order ~w(...)`):
21+
22+
```elixir
23+
@testimonials [
24+
%{
25+
body:
26+
"Crit saves me so much time reviewing Claude Code plans - instead of fumbling with line numbers or accidental sends, I get a clean local UI to batch my feedback and iterate, all without leaving my workflow.",
27+
author: "Omer",
28+
handle: "omervk"
29+
},
30+
%{
31+
body:
32+
"I've been using crit to review plans for some times. I use claude code in the command line without an IDE, so being to quickly check the plan with rendering is super nice. The system allowing you to add comments is the killer feature: it's like a pull request review but for your plan. On long, complex plans I used to ask claude things like \"on point 3., we should do X, drop point 7., ...\". Using comments makes it more straightforward and easy to review later.",
33+
author: "Vincent",
34+
handle: "vineus"
35+
}
36+
]
37+
```
38+
39+
- [ ] **Step 2: Pass `@testimonials` to the template in the `home/2` function**
40+
41+
In the `home/2` function's `render` call (line 146), add `testimonials: @testimonials` to the assigns:
42+
43+
```elixir
44+
render(conn, :home,
45+
demo_token: Application.get_env(:crit, :demo_review_token),
46+
testimonials: @testimonials,
47+
canonical_url: canonical_url(conn),
48+
...
49+
```
50+
51+
- [ ] **Step 3: Verify compilation**
52+
53+
Run: `mix compile --warnings-as-errors`
54+
Expected: Compiles cleanly with no warnings.
55+
56+
- [ ] **Step 4: Commit**
57+
58+
```bash
59+
git add lib/crit_web/controllers/page_controller.ex
60+
git commit -m "feat: add testimonials data to page controller"
61+
```
62+
63+
---
64+
65+
### Task 2: Add testimonials section to homepage template
66+
67+
**Files:**
68+
- Modify: `lib/crit_web/controllers/page_html/home.html.heex:597` (insert before `<!-- ===== Self-Hosting ===== -->`)
69+
70+
- [ ] **Step 1: Insert testimonials section before the Self-Hosting section**
71+
72+
Insert the following block between line 596 (`</section>` closing the Features section) and line 598 (`<!-- ===== Self-Hosting ===== -->`):
73+
74+
```heex
75+
<%!-- ===== Testimonials ===== --%>
76+
<section class="max-w-[1100px] mx-auto mt-14 mb-8 px-10 max-sm:px-5 w-full">
77+
<p class="font-mono text-xs tracking-widest uppercase text-(--crit-accent) mb-6">
78+
What engineers are saying
79+
</p>
80+
<div class="grid grid-cols-2 gap-4 max-sm:grid-cols-1">
81+
<div
82+
:for={testimonial <- @testimonials}
83+
class="rounded-lg border border-(--crit-border) bg-(--crit-bg-secondary) p-6"
84+
>
85+
<p class="text-sm leading-relaxed text-(--crit-fg-primary) mb-4">
86+
"<%= testimonial.body %>"
87+
</p>
88+
<p class="font-mono text-xs text-(--crit-fg-muted)">
89+
<%= testimonial.author %>
90+
<a
91+
href={"https://x.com/#{testimonial.handle}"}
92+
class="text-(--crit-fg-dimmed) hover:text-(--crit-accent) transition-colors ml-1"
93+
target="_blank"
94+
rel="noopener noreferrer"
95+
>
96+
@<%= testimonial.handle %>
97+
</a>
98+
</p>
99+
</div>
100+
</div>
101+
</section>
102+
```
103+
104+
- [ ] **Step 2: Start the dev server and verify visually**
105+
106+
Run: `dev up` (or `mix phx.server` if already set up)
107+
Open: `http://localhost:4000`
108+
Expected: Scrolling down past the features grid and compact feature strip, there's a "What engineers are saying" section with two cards side-by-side. Omer's quote appears first (left), Vincent's second (right). On mobile viewport, they stack vertically.
109+
110+
- [ ] **Step 3: Verify formatting checks pass**
111+
112+
Run: `mix format --check-formatted`
113+
Expected: No formatting issues.
114+
115+
- [ ] **Step 4: Commit**
116+
117+
```bash
118+
git add lib/crit_web/controllers/page_html/home.html.heex
119+
git commit -m "feat: add testimonials section to homepage"
120+
```
121+
122+
---
123+
124+
### Task 3: Run full precommit checks
125+
126+
- [ ] **Step 1: Run precommit**
127+
128+
Run: `mix precommit`
129+
Expected: All checks pass (compile, format, sobelow, audit, test).
130+
131+
- [ ] **Step 2: Fix any issues**
132+
133+
If any check fails, fix and re-run until clean.
134+
135+
- [ ] **Step 3: Final commit if any fixes were needed**
136+
137+
```bash
138+
git add -A
139+
git commit -m "fix: address precommit issues"
140+
```
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Testimonials Section Design
2+
3+
## Overview
4+
5+
Add a "What engineers are saying" testimonials section to the homepage, positioned between the demo video and the install widget. Minimal quote cards styled with Tailwind and `--crit-*` CSS variables.
6+
7+
## Placement
8+
9+
Homepage (`home.html.heex`), between the demo video section and the install widget section.
10+
11+
## Data
12+
13+
Testimonials defined as a list of maps in `page_controller.ex`, passed to the template as an assign. Each map contains:
14+
15+
- `body` — the quote text
16+
- `author` — display name
17+
- `handle` — X/Twitter handle (without @)
18+
- `url` — link to the person's X profile
19+
20+
Initial testimonials (in this order):
21+
22+
1. **Omer (@omervk)**: "Crit saves me so much time reviewing Claude Code plans - instead of fumbling with line numbers or accidental sends, I get a clean local UI to batch my feedback and iterate, all without leaving my workflow."
23+
2. **Vincent (@vineus)**: "I've been using crit to review plans for some times. I use claude code in the command line without an IDE, so being to quickly check the plan with rendering is super nice. The system allowing you to add comments is the killer feature: it's like a pull request review but for your plan. On long, complex plans I used to ask claude things like \"on point 3., we should do X, drop point 7., ...\". Using comments makes it more straightforward and easy to review later."
24+
25+
## Visual Design
26+
27+
### Section heading
28+
29+
- Monospace, uppercase, tracked-wide, accent-colored eyebrow: "What engineers are saying"
30+
- Matches the hero eyebrow style ("Local-first . No login . Works with any agent")
31+
32+
### Quote cards
33+
34+
- Grid: `grid grid-cols-2` on desktop, single column on mobile
35+
- Card: `--crit-bg-secondary` background, `--crit-border` border, rounded corners
36+
- Quote text: regular body font, `--crit-fg-primary`, natural reading size
37+
- Author line: name + linked X handle in `--crit-fg-muted`, monospace
38+
- No photos, no star ratings, no logos
39+
- Consistent padding matching surrounding sections
40+
41+
### Responsive
42+
43+
Follows the existing homepage pattern: 2 columns on desktop, 1 column on mobile using `max-sm:grid-cols-1`.
44+
45+
## Growth
46+
47+
Adding a testimonial = adding a map to the list in `page_controller.ex`. No template changes needed. The `:for` comprehension renders whatever is in the list.
48+
49+
## What This Does NOT Include
50+
51+
- No dedicated `/testimonials` or `/wall-of-love` route
52+
- No database table
53+
- No new CSS classes in `app.css` (Tailwind only)
54+
- No carousel, rotation, or animation
55+
- No photos or avatars

lib/crit_web/controllers/page_controller.ex

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,36 @@ defmodule CritWeb.PageController do
139139

140140
@feature_order ~w(inline-comments split-unified-diff ai-review-loop vim-keybindings share-reviews syntax-highlighting mermaid-diagrams)
141141

142+
@testimonials [
143+
%{
144+
highlight: "A clean local UI to batch my feedback and iterate.",
145+
body: [
146+
"Crit saves me so much time reviewing Claude Code plans - instead of fumbling with line numbers or accidental sends, I get a clean local UI to batch my feedback and iterate, all without leaving my workflow."
147+
],
148+
author: "Omer",
149+
role: "Principal Engineer",
150+
handle: "omervk"
151+
},
152+
%{
153+
highlight: "It's like a pull request review but for your plan.",
154+
body: [
155+
"I've been using crit to review plans for some times. I use claude code in the command line without an IDE, so being to quickly check the plan with rendering is super nice.",
156+
"The system allowing you to add comments is the killer feature: it's like a pull request review but for your plan.",
157+
"On long, complex plans I used to ask claude things like \"on point 3., we should do X, drop point 7., ...\". Using comments makes it more straightforward and easy to review later."
158+
],
159+
author: "Vincent",
160+
role: "Senior Software Engineer",
161+
handle: "vineus"
162+
}
163+
]
164+
142165
def home(conn, _params) do
143166
if Application.get_env(:crit, :selfhosted) do
144167
redirect(conn, to: "/dashboard")
145168
else
146169
render(conn, :home,
147170
demo_token: Application.get_env(:crit, :demo_review_token),
171+
testimonials: @testimonials,
148172
canonical_url: canonical_url(conn),
149173
page_title: "Crit - Inline code review for AI coding agents",
150174
meta_description:

lib/crit_web/controllers/page_html/home.html.heex

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,57 @@
594594
</div>
595595
</div>
596596
</section>
597-
598-
<!-- ===== Self-Hosting ===== -->
597+
598+
<%!-- ===== Testimonials ===== --%>
599+
<section class="w-full mt-14 mb-8 py-12 bg-(--crit-bg-secondary)">
600+
<div class="max-w-[1100px] mx-auto px-10 max-sm:px-5">
601+
<p class="font-mono text-xs tracking-widest uppercase text-(--crit-accent) mb-10">
602+
What engineers are saying
603+
</p>
604+
<div class="grid grid-cols-2 gap-12 max-sm:grid-cols-1 max-sm:gap-10" id="testimonials-grid">
605+
<div :for={testimonial <- @testimonials} class="feature-card relative">
606+
<span
607+
class="absolute -top-4 -left-3 text-[8rem] leading-none text-(--crit-accent) opacity-10 select-none pointer-events-none"
608+
aria-hidden="true"
609+
>
610+
&ldquo;
611+
</span>
612+
<p class="relative text-xl font-semibold leading-snug text-(--crit-fg-primary) mb-5">
613+
{testimonial.highlight}
614+
</p>
615+
<div class="relative space-y-3 mb-5">
616+
<p
617+
:for={paragraph <- testimonial.body}
618+
class="text-sm leading-relaxed text-(--crit-fg-muted)"
619+
>
620+
{paragraph}
621+
</p>
622+
</div>
623+
<p class="relative font-mono text-xs text-(--crit-fg-muted)">
624+
&mdash; {testimonial.author}, {testimonial.role}
625+
<a
626+
href={"https://x.com/#{testimonial.handle}"}
627+
class="text-(--crit-fg-dimmed) hover:text-(--crit-accent) transition-colors ml-1"
628+
target="_blank"
629+
rel="noopener noreferrer"
630+
>
631+
@{testimonial.handle}
632+
</a>
633+
</p>
634+
</div>
635+
</div>
636+
<p class="mt-10 text-xs text-(--crit-fg-dimmed)">
637+
Using crit? I'd love to hear from you.
638+
<a
639+
href="mailto:me@tomasztomczyk.com?subject=Crit%20testimonial"
640+
class="underline underline-offset-2 hover:text-(--crit-fg-muted) transition-colors"
641+
>
642+
me@tomasztomczyk.com
643+
</a>
644+
</p>
645+
</div>
646+
</section>
647+
<!-- ===== Self-Hosting ===== -->
599648
<section
600649
class="max-w-[1100px] mx-auto mt-2 mb-16 px-10 max-sm:px-5 w-full"
601650
id="self-hosting-card"

0 commit comments

Comments
 (0)