# Reviews & Projects

## Reviews & Projects

**Files:** `src/content/reviews/english/`, `src/content/sections/english/trust-strip.md`, `src/content/projects/english/` · **Skill level:** Beginner

Two collections show proof of your work. **Reviews** are what customers wrote, one file per review. **Projects** are finished jobs with a before and an after photo, one file per job.

<Aside type="caution" title="Every review and project is a sample">
  The shipped reviews, ratings, review counts, customer quotes, and project
  photos are examples. Replace them with real ones (with the customer's consent)
  or delete them before you launch. Never publish made-up reviews.
</Aside>

### A Review
Each review is one file in `src/content/reviews/english/`. The file name is free; the samples use `date-name-service.md`:

```yaml
---
# Sample review: replace with a real one, with the customer's consent.
name: "Tomas B." # sample; first name and last initial only, no photo
area: "north-harbor" # area id in src/content/areas/ (free text is shown as written)
service: "standard-cleaning" # service id in src/content/services/; drives the filter chips
rating: 5 # whole number, 1 to 5
source: "search" # rating source id from sections/trust-strip.md ("via ..." line)
date: 2025-10-17 # shown as month and year
featured: false # featured band on /reviews/, first pick on the homepage
text: "Two-bedroom flat, two people, just under three hours. …" # up to 700 characters
draft: false
---
```

| Field      | What it does                                                                                  |
| ---------- | --------------------------------------------------------------------------------------------- |
| `name`     | The reviewer's first name and last initial. Reviews never show a photo                        |
| `area`     | An area id (the review then shows on that area's page) or plain text                          |
| `service`  | A service id. Sets the filter chip on `/reviews/` and shows the review on that service's page |
| `rating`   | A whole number from 1 to 5                                                                    |
| `source`   | Where the review came from, as a source id from `trust-strip.md` (see below)                  |
| `date`     | The date of the review, shown as month and year                                               |
| `text`     | The review, up to 700 characters. `npm run astro-check` reports a longer one                  |
| `featured` | Puts the review in the featured band on `/reviews/` and first on the homepages                |
| `reply`    | Optional answer from you, shown under the review: `reply: { text: "…", name: "Ana, office" }` |

Each language has its own review files. Copy each file to `src/content/reviews/french/` with the same name, translate `text` (and `reply`), and keep the other fields the same.

### Where Reviews Appear
| Place         | Which reviews                                                                              | Set in                                                                    |
| ------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| `/reviews/`   | Every review, with service chips, a featured band, and the ratings summary (`wall` layout) | `src/content/pages/english/reviews.md`                                    |
| Homepages     | The newest and featured reviews                                                            | `reviewsSection` in each homepage file                                    |
| Service pages | Reviews with that `service`. With fewer than 2, reviews of the same trade fill in          | `reviewsSection` in `detail:` of `src/content/services/english/-index.md` |
| Area pages    | Reviews with that `area`                                                                   | The area file                                                             |

The section has eight layouts (`default`, `marquee`, `wall`, `summary`, `carousel`, `spotlight`, `single`, `band`); see [Section Layouts](/homio/customization/section-layouts/). Its options:

```yaml
reviewsSection:
  summary: true # ratings ledger from sections/trust-strip.md
  filter:
    service: "" # service id; empty = every service
    area: "" # area id; empty = every area
    excludeFeatured: true # featured reviews render in the featured band only
  limit: 0 # 0 = every review
  options:
    layout: "wall"
```

### The Rating Summary
The average rating, the review count, and the per-source ratings are **typed**, not counted from the review files. You copy them from the places where customers review you. They are in `src/content/sections/english/trust-strip.md`:

```yaml
rating:
  enable: true
  value: 4.9 # sample average out of 5
  count: 1240 # sample number of reviews
  label: "from {count} reviews" # {count} and {value} are replaced
  url: "/reviews/"
  sources: # Generic labels only; no third-party logos or names
    # id: matched by review entries' `source` (reviews page); url: optional "Write a review" link
    # updated: optional date the numbers were last copied
    - id: "search"
      label: "Search reviews"
      value: 4.9 # sample
      count: 820 # sample
      url: "https://example.com/review"
      updated: 2026-09-01
```

- `value` and `count` feed the rating chip on the homepage hero, the trust strip, and the summary on `/reviews/`.
- Each source is one row of the ratings summary. `url` adds a "Write a review" link; `updated` is the date you last copied the numbers.
- Keep the labels generic. The shipped file uses "Search reviews", "Neighbourhood app", and "After-visit survey" instead of third-party names or logos.
- Update the numbers when they change. A rating that is out of date or higher than your real one misleads customers.

### Why Reviews Have No Rich Results Markup
Homio adds no `Review` or `AggregateRating` structured data, on purpose. These are reviews you collect and publish on your own site about your own business. Google does not show review stars for those ("self-serving" reviews), and marking them up anyway goes against its review snippet guidelines. The reviews still help: visitors read them, and search engines read the text. Your star rating in search comes from your business profile and the review sites themselves.

If a review platform you use offers its own widget, follow its terms. Don't copy its reviews into review markup.

### Projects (Before And After)
Each project is one file in `src/content/projects/english/` and one page at `/projects/<file-name>/`. From `bathroom-regrout.md`:

```yaml
title: "Shower re-grout and re-caulk"
description: "Cracked grout lines and a mouldy silicone seal around a tiled shower."
date: 2026-08-27 # job date: sorts the grid (newest first) and shows on the ticket
ticket: "4139" # job number, shown as #4139
trade: "repairs" # trade id from `trades:` in src/content/services/english/-index.md
service: "handyman" # service entry id: chip, service link, and /book/?service=
area: "old-mill" # area entry id from src/content/areas/
featured: false # the large card on /projects/ and the homepage job (first `true` wins)
before: "/images/projects/shower-grout-before.jpg" # same crop and angle as `after`
after: "/images/projects/shower-grout-after.jpg"
beforeAlt: "Shower tiles before: dull, stained grout lines"
afterAlt: "The same tiles after: fresh grout lines, tiles cleaned"
caption: "Shower wall, after new grout and a fresh seal" # under the large slider
duration: "4 h" # time on site, shown as written
crewSize: 1
scope: # check rows on the job ticket, in work order
  - "Old silicone cut out and the joint cleaned"
  - "New silicone seal at the tray"
price: # optional; remove to hide the total row
  amount: 310 # sample
  note: "Hourly rate, materials included"
quote: # optional customer words about this job
  text: "He showed me the soft grout he had raked out …"
  name: "Tomás L." # first name and last initial, no photo
  detail: "Old Mill"
```

The Markdown body is the write-up ("The job", "What we did", "The result"). `gallery` adds more before/after pairs as static frames:

```yaml
gallery: # optional extra pairs, shown as static split frames
  - before: "/images/projects/range-degrease-before.jpg"
    after: "/images/projects/range-degrease-after.jpg"
    beforeAlt: "Range before: grates and steel trim dulled by grease"
    afterAlt: "The same range after: grates and trim degreased"
    caption: "Range and grates"
```

`price.amount` is a plain number in your currency. It is typed, not computed from the pricing file.

**Where projects appear:** `/projects/` (a grid with trade buttons and the featured project large, set in `src/content/projects/english/-index.md`), the "Similar jobs" block on each project page (`relatedProjects` in the same file), a recent job on service pages (matched by `service`), area pages (matched by `area`), industry pages (matched by `industry`), and the handyman homepage. A page with no matching project hides the block.

The section's layouts are `default` (one large slider beside the job ticket), `grid` (project cards), and `gallery`. Its defaults, including the frame shape, are in `src/content/sections/english/before-after-section.md`:

```yaml
options:
  layout: "default" # default | grid | gallery (masonry of job photos, button)
  aspect: "3/2" # slider frame (default layout): 4/5 | square | 3/2 | 4/3
  start: 50 # divider start, 0–100 % from the start edge
```

### Align A Before/After Pair
The slider stacks the after photo on the before photo and reveals it as the visitor drags the line (or uses the arrow keys). Both photos are cropped to the same frame (`aspect`), from the center. If the two photos differ, the line shows a jump instead of a change.

<Steps>

1. **Shoot both from the same spot.** Same position, height, lens, and zoom. Mark the spot (a piece of tape on the floor) or take the before photo again from the same marks before you start work.
2. **Keep the same orientation.** Both landscape or both portrait.
3. **Crop both the same way.** Crop the pair to the same aspect ratio and the same size, ideally the ratio of the frame (3:2 by default, square on service and area pages). The build prints a `[BeforeAfter]` warning when the two ratios differ by more than 5%.
4. **Save them in `src/assets/images/projects/`** with matching names (`kitchen-before.jpg`, `kitchen-after.jpg`), and write the paths as `/images/projects/kitchen-before.jpg`.
5. **Write both alt texts** so the difference is clear without the image: "Before: …" and "The same … after: …".
6. **Check the result** at `/projects/<file-name>/`. Drag the line across a fixed point (a tap, a tile edge). It should not move between the two photos.

</Steps>

A project with no `after` photo shows the before photo alone, without a slider, and the build prints a warning.

<Aside type="note" title="The sample pairs are placeholders">
  In the shipped projects, each "before" photo is the "after" photo darkened and
  desaturated (`IMAGE-CREDITS.md` lists them). Replace both photos of every pair
  with your own.
</Aside>

To add a project, copy a project file, rename it (the name is the URL), change the fields and photos, and copy it to `src/content/projects/french/` with the same name. The `service`, `area`, `trade`, and `industry` values must be ids that exist, or the project won't show on those pages.

### Do It With AI
```text
Replace the sample reviews with these real ones (I have each customer's consent): [NAME, AREA,
SERVICE, RATING, SOURCE, DATE, TEXT; ...]. One file per review in src/content/reviews/english/,
with the service and area ids that exist in my content. Delete the samples. Then set the rating
in src/content/sections/english/trust-strip.md to [AVERAGE] from [COUNT] reviews, with sources
[LABEL: VALUE, COUNT, URL; ...]. Do the French files too.
```

```text
Add a project: [TITLE], [SERVICE] in [AREA], [DATE], job number [N], [CREW] crew, [TIME] on site,
scope [STEPS], price [AMOUNT or none]. My photos are [BEFORE FILE] and [AFTER FILE]. Copy them
to src/assets/images/projects/, check they have the same aspect ratio and tell me if not, then
create the project file from src/content/projects/english/bathroom-regrout.md and its French copy.
```