Content model

How content is structured in this site: which collections exist, which fields they use, and the conventions that keep templates and listings consistent.

Collections in this site

Collections are defined in /src/content/config.ts. Content entries live in /src/content/<collection>/.

Collection Folder Used for
services /src/content/services/ Service pages and service listings (including homepage sections)
portfolio /src/content/portfolio/ Case studies and the portfolio index
thoughts /src/content/thoughts/ Thoughts/blog posts and the thoughts index
resources /src/content/resources/ Resources and the resources index
clients /src/content/clients/ Client entries used by the client logo grid
pages /src/content/pages/ Generic page metadata used across layouts/templates

Where content lives

Content entries live in /src/content/. Each collection has its own folder (for example /src/content/portfolio/).

Conventions

  • Filenames matter — in most cases the entry slug defaults to the filename.
  • Frontmatter is validated — schemas in /src/content/config.ts decide which fields are allowed/required.
  • Images are imported and optimised — image fields defined with image() expect an asset in /src/assets/images/.

Services

Services are the primary building blocks for service pages and navigation. They are also used to tag portfolio entries.

Key fields

  • title — full name.
  • slug (optional) — URL override (defaults to filename).
  • order — controls ordering in listings.
  • shortLabel, heading, teaser, description — copy used across the site.
  • icon — image metadata (from /src/assets/images/).

Adding a new service

  • Create a new file in /src/content/services/.
  • Set an order value so it appears in the right place.
  • Provide an icon image (in /src/assets/images/) that matches the frontmatter reference.

Portfolio

Portfolio entries are case studies. They can reference one or more services for filtering and “related work”.

Key fields

  • title, subtitle
  • date (optional) — used for sorting (newest first).
  • client (optional)
  • excerpt (optional)
  • featuredImage (optional) — image metadata for listing thumbnails.
  • services (optional) — references services collection items.
  • quote, quoteAttribution, quoteBackground (optional) — for in-page callouts.

Service tagging

Portfolio entries can be tagged with multiple services. This powers filtering on the portfolio index and supports “related case studies” lists.

Thoughts

Long-form posts. These support optional “further reading” and an optional featured link block.

Key fields

  • title, subtitle (optional)
  • date (optional), author (optional)
  • categories (optional)
  • excerpt (optional)
  • heroImage (optional), heroColor (optional)
  • furtherReading (optional), furtherReadingTitle (optional)
  • featuredLink (optional) — includes a screenshot filename and optional alt text.

Resources

Resources are similar to thoughts, but with a simpler metadata shape.

Key fields

  • title, subtitle (optional)
  • date (optional), author (optional)
  • excerpt (optional)
  • heroImage (optional), heroColor (optional)

Clients

Client entries feed the client logo grid. Logos are typically named to match the client slug and stored in /src/assets/images/.

Key fields

  • title
  • link (optional)
  • logo (optional) — currently a string field.

Pages

Generic page metadata used by templates/layouts where needed.

Key fields

  • title
  • subtitle (optional)
  • description (optional)

Querying patterns

Most listings load entries with getCollection, then filter/sort in code.

Typical query

import { getCollection } from 'astro:content';

const allPortfolio = await getCollection('portfolio');

const sorted = allPortfolio
  .filter((entry) => entry.data.featuredImage)
  .sort((a, b) => (b.data.date?.valueOf() ?? 0) - (a.data.date?.valueOf() ?? 0));

Images

Some frontmatter fields are validated as image() types. Those expect a file in /src/assets/images/ and will be optimised by the site’s shared image utilities/components.

What to use for new entries

  • Prefer image fields typed as image() where possible (typed, optimisable, and harder to break).
  • Keep filenames stable to avoid broken references.
  • Provide meaningful alt text at render time (for example when using hero images).

References between content

Some collections reference others for stronger relationships. For example, portfolio entries use reference('services') so service filtering is reliable and type-safe.

Practical impact

  • Renaming a service can affect linked portfolio entries (because they reference the service entry).
  • Deleting a service may invalidate portfolio entries that reference it.