prelo
Plugins / Testimonials

Testimonials

Collect and curate client quotes, rendered as a wall or a carousel.

Tgithub.com/tbakerMarketingprelo >= 0.4Updated May 2026
Use this plugin
1
2
or point your agent at the spec:
Your agent reads the spec and writes the code into your project. Review the diff like any change.

What this adds

A testimonials system: quotes live as content the owner curates in Studio, and land on the site as either a quote wall or a swipeable carousel — placed on any page as a section. Optionally, a public "share your experience" form collects new quotes straight into the moderation queue.

How it works

A testimonial content type holds the quotes (draft = hidden, published = live). A testimonials section definition lets the client drop the block onto pages and choose wall vs carousel per placement. The optional collection form posts to a rate-limited custom route that stores submissions as drafts.

The spec your agent followsexpand ▾
You are adding testimonials to a Prelo project.

1. **Content type.** In `cms.config.js`:

   ```js
   testimonial: {
     label: "Testimonials",
     fields: {
       quote:    { type: "text", required: true },
       person:   { type: "text", required: true },
       role:     { type: "text" },            // "CEO, Acme"
       avatar:   { type: "image" },
       featured: { type: "select", options: ["yes", "no"] },
     },
     studio: { columns: ["person", "role", "featured"] },
   },
   ```

2. **Section.** Under `sections:`:

   ```js
   testimonials: {
     fields: {
       heading: { type: "text" },
       layout:  { type: "select", options: ["wall", "carousel"], studio: "variant" },
       only:    { type: "select", options: ["all", "featured"] },
       max:     { type: "number" },
     },
   },
   ```

3. **Renderer.** In the project's section renderer map (`renderBody(body, { testimonials: … })` for one-process sites, or the frontend's section switch):
   - Fetch published testimonials (`GET /api/testimonial`), filter to `featured: "yes"` when `only` = featured, cap at `max` (default 12), newest first.
   - **Wall:** CSS multi-column layout (3 → 2 → 1 columns responsive), each card: quote, then avatar (`?w=96`, rounded) + person + role in the project's muted style. No JS.
   - **Carousel:** a horizontal scroll-snap track of the same cards, with prev/next buttons that `scrollBy` one card and hide when scrolling isn't possible. Must degrade to plain horizontal scrolling without JS. No carousel library.
   - Render quotes in `<figure>`/`<blockquote>`/`<figcaption>` for semantics; escape everything.

4. **Collection form (optional — confirm with the user before building).** If wanted:
   - `hooks.boot`: `x_testimonial_hits (ip TEXT, at INTEGER)`; limit 2 per hour per IP.
   - Route `"POST /testimonials"`: validate quote 10–600 chars, person required, honeypot `website` silently discarded; insert as a **draft** `testimonial` (title = person). Respond `{"ok":true}`.
   - A small form component (quote textarea, name, role, honeypot) with a "Thanks — we review every quote before it appears" success state.

5. **Verify.** Seed 4–5 testimonials (draft one of them), add a section in Studio in each layout, confirm the draft never renders, the featured filter works, and the carousel is keyboard/scroll accessible.

After it's built

The owner curates in Studio: publish to show, draft to hide, flip featured to build a shortlist for the homepage while the full wall lives elsewhere. Submitted quotes (if enabled) wait as drafts until reviewed.