Editing the content — Kalme Astro
All the words and images of the site live in src/content/. You edit plain JSON files — text in quotes, commas between
entries — and the site updates while npm run dev is running. No CMS is needed. (If you would rather manage content in
Sanity, Payload, Strapi or Directus, see Using a headless CMS.)
src/content/ site.json name, description, domain, navigation, contact details, footer, call to action pages/home.json the copy of the home page pages/about.json …and of every other page collections/*.json services, projects, posts, team, testimonials, legal texts schemas.ts the fields each kind of item has, and which are requiredNot sure which file holds a piece of text? The page maps label every section of every page with its file.
Your details first
Section titled “Your details first”src/content/site.json holds what appears on every page:
| Field | Shown where |
|---|---|
site.name |
Logo alt text, browser title suffix, share cards |
site.title |
Browser title of the home page |
site.description |
Search results and share cards, for pages without their own description |
site.url |
Set this to your domain before publishing. Canonical links, share links, sitemap.xml and robots.txt are built from it |
navigation |
The menu in the header (and the phone menu) |
headerAction |
The button on the right of the header |
contact |
E-mail, phone and address — used on the Contact page and in the footer |
socials |
The social links in the footer |
footer |
The text and button next to the logo, the quick links, the column titles, the copyright line ({year} becomes the current year) and the legal links |
cta |
The call to action above the footer on Home and every project page: label, title, text, the two buttons and the six images of the ticker |
site.url is read by astro.config.mjs at build time, so restart npm run dev after changing it.
The demo’s e-mail (hello@example.com), phone number and address are placeholders: replace all three. The buttons that
book a call (“Schedule a call”, “Book free consultation”, “Book an intro call”) link to https://cal.com/: put your own
booking link in footer.action, cta.actions and action in src/content/pages/service.json.
Page copy
Section titled “Page copy”One file per page in src/content/pages/. Each holds the sections of that page and, for pages with a fixed URL, a
meta entry:
{ "meta": { "title": "About", "description": "Kalme is a London digital marketing agency…" }, "hero": { "…": "…" }}title becomes the browser tab (“About | Kalme”) and description the text in search results and share cards (80–160
characters). Change what is inside the quotes and keep the field names — the sections read them by name. JSON has no
comments and no trailing commas; if the site stops building, that is usually why.
| File | Page |
|---|---|
home.json |
Home (it has no meta: its title and description are site.title and site.description) |
about.json, services.json, projects.json, blog.json, contact.json |
The pages of the same name |
service.json, project.json, post.json |
The words shared by every service, project and post page (headings, labels, “More Services”) |
legal.json |
The date label and the search descriptions of the two legal pages |
not-found.json |
The 404 page |
Collections
Section titled “Collections”src/content/collections/ holds the lists the site builds pages from:
| File | What it feeds |
|---|---|
services.json |
The Services page, one page per service, the accordion on Home (first five) and “More Services” at the end of a service page (the first four others) |
projects.json |
The Projects page, one page per project, Home (first four), the showreel in the Home hero (the first four project images) and “Related Projects” (the first four others) |
posts.json |
The Blog, one page per post, Home (first three) and “More Blog” at the end of a post (up to three of the same category) |
testimonials.json |
The testimonial cards on Home, in the order of the file: dark, wide, wide, light, dark |
team.json |
The team grid on About |
legal.json |
Terms of Service and Privacy Policy, one page each (/legal/<slug>) |
The order of the items in the file is the order on the site. The numbers (five, four, three) are in
src/pages/index.astro and in the service, project and post pages under src/pages/.
Adding a service
Section titled “Adding a service”Copy an existing entry, paste it below (with a comma between entries), and change the values:
{ "slug": "brand-strategy", "number": "09", "title": "Brand Strategy", "images": ["/images/service-brand-1.jpg", "/images/service-brand-2.jpg"], "description": "Find the position your brand can own…", "tools": [{ "label": "Figma", "href": "https://www.figma.com/" }], "offer": [{ "highlight": "Brand Audit", "text": ": Where your brand stands today." }], "benefits": [{ "highlight": "Clarity", "text": ": One message across every channel." }], "plans": [ { "id": "weekly", "label": "Weekly", "price": "$500", "note": "per week", "included": ["Weekly strategy call"] }, { "id": "monthly", "label": "Monthly", "price": "$1,800", "note": "per month", "included": ["…"] } ]}slug is the URL (/services/brand-strategy) and number is shown in the Home accordion. The first image is used on
the cards and the service page (about 1920 × 1280), the second in the Home accordion (about 1200 × 800). offer and
benefits are the bullets of “What We Offer” and “Key Benefits”: highlight is the bold start of a bullet, text the
rest (start it with “: “ to keep the demo’s punctuation). plans feeds the pricing card: with two plans it shows the
switch between them, with one plan only its price. Prices are text, so write them with your own currency.
Adding a project
Section titled “Adding a project”{ "slug": "northwind-rebrand", "title": "Northwind Rebrand", "year": "2025", "image": "/images/project-northwind.jpg", "services": [{ "label": "Content Marketing", "slug": "content-marketing" }], "summary": "Kalme gave Northwind…", "industry": "Retail", "overview": { "text": "…", "image": "/images/project-northwind-overview.jpg" }, "process": { "text": "…", "images": ["/images/project-northwind-process-1.jpg", "/images/project-northwind-process-2.jpg"] }, "result": { "items": [{ "highlight": "Traffic", "text": ": up 3× in six months." }], "image": "/images/project-northwind-result.jpg" }}image is the card and the top of the project page (3:2, about 1920 × 1280); summary is shown under the title and is
also the search description. Each entry in services links to /services/<slug>, so use the slug of a service that
exists.
Adding a post, team member, testimonial or legal text
Section titled “Adding a post, team member, testimonial or legal text”| Add | Fields |
|---|---|
| Post | slug, title, excerpt (the card text and the search description), image (portrait, about 1200 × 1800), category (any words; posts with the same one are shown as “More Blog” under each other), date ("2026-05-16"), body |
| Team member | name (unique: it identifies the member), role, email, photo (portrait 2:3, about 800 × 1200) |
| Testimonial | slug, quote, name, company, service, rating (1–5 stars), photo (round, about 200 px), and logo (a company logo, shown on the two wide cards) |
| Legal text | slug, title, date ("2025-08-10"), body; its search description goes in descriptions in src/content/pages/legal.json |
Home has five testimonial places around the video: the first five testimonials fill them, in order.
Removing an item
Section titled “Removing an item”Delete the entry (and the comma before it, if it was the last one). Its page, its card and its sitemap entry disappear
on the next build. A project names its services by slug: if you delete a service, search the project files for its
slug (Ctrl/Cmd + Shift + F) and remove it from their services, or the tag on the project page links to a page that
no longer exists.
What each field means
Section titled “What each field means”src/content/schemas.ts is the reference: it lists every field and which are optional (.optional()). npm run check
and npm run build stop on an entry that does not match and name the file and the field.
Rich text: posts and legal texts
Section titled “Rich text: posts and legal texts”A post body or a legal text is a list of blocks:
"body": [ { "type": "heading", "text": "1. Information we collect" }, { "type": "paragraph", "text": "A plain paragraph." }, { "type": "paragraph", "highlight": "Bold start:", "text": " and the rest of the paragraph." }, { "type": "list", "items": [ [{ "type": "paragraph", "text": "First item" }], [{ "type": "paragraph", "highlight": "Second:", "text": " with a bold start" }] ] }]Add "ordered": true to a list for numbers. Each list item is itself a list of blocks, so a numbered item can hold a
heading, paragraphs and a nested list. A heading can end in a bolder part with "bold": "…". Text is plain: the bold
parts are highlight and bold, not **stars**.
Videos
Section titled “Videos”Three videos play on the site. Each has a src (a web address or a file) and a poster (the picture shown before it
plays):
| Video | Where it plays | Field |
|---|---|---|
| Page video | Home, under the hero; it rises as you scroll | video in src/content/pages/home.json |
| Portrait clip | Home, between the testimonial cards | testimonials.video in home.json |
| Studio video | About, between the values and the team | video in src/content/pages/about.json |
The demo plays stock clips from the web; they are not part of the download. To use your own, put an MP4 in
public/videos/ and set "src": "/videos/showreel.mp4". Keep the proportions of the demo (16:9 for the two wide ones,
9:16 for the portrait clip), export at 1280 px wide or so, and keep each file small (a few MB): they play muted and in a
loop. Replace the matching poster in src/assets/images/ too.
Images
Section titled “Images”- Put the file in
src/assets/images/. - Refer to it in the content as
/images/<file>.
Astro finds the file by its name, then resizes it and serves a modern format at the sizes the layout needs, when the site
builds. Use photos at least as wide as they appear (project and service photos: 1920 px, other photos 1200 px,
portraits 800 px), in .jpg for photos and .png when you need transparency. Keep the demo’s aspect ratios (the layout
crops to fit).
Two things sit outside the content because browsers and social networks ask for them by name:
| File | What it is |
|---|---|
public/icon.svg, public/apple-icon.png |
The icon in the browser tab and on a phone’s home screen |
public/opengraph-image.png |
The picture shown when someone shares a link (2400 × 1260; 1200 × 630 or larger) |
Its description for screen readers is shareImageAlt in src/layouts/Layout.astro. Every page uses this image when
shared, except the service, project and post pages, which share their own image.
The search-result description of a service is its description, of a project its summary, and of a post its
excerpt (cut at 160 characters) — start each with a real sentence.
Text that is not in content/
Section titled “Text that is not in content/”Almost every word is content, including the form’s messages (contact.json → form) and the labels of the service
pages (service.json → labels). The few interface words that never change per site — “Menu” and “Close” on the phone
menu button — live with the components in src/design-system/. Search for the phrase and you will find it.
After editing
Section titled “After editing”npm run buildA successful build means every page, link and image still resolves. See Going live for the rest of the checklist.