Skip to content

Case studies

Each case study is one JSON file in src/content/projects/. The file name is the URL: halden-rail.json becomes /case-study/halden-rail. Every file is checked against a schema when you build, so a missing field or a typo fails the build with a clear message instead of breaking a page.

You can write them by hand or in the Keystatic editor, which writes exactly the same files.

  • Directorysrc/
    • Directorycontent/projects/
      • halden-rail.json the English case study
      • Directoryde/
        • halden-rail.json the German translation, same file name
    • Directoryassets/projects/halden-rail/
      • cover.jpg
      • Directorygallery/
        • 0/src.jpg
        • 1/src.jpg
Field Type Notes
client text The client’s name. Shown in the index, on cards and in the page header
title text The headline of the case study
summary text, max 160 characters One sentence for cards, search results and social previews
year number Year of the project
order number Position on the site. Lower comes first
featured true / false The first featured project leads the home page. Default false
layout editorial, narrative, gallery Page layout, see below. Default editorial
disciplines list, at least one From the list in src/content/taxonomy.ts. Used by the work filters
industry one value From the list in taxonomy.ts. Used by the work filters
services list of text, at least one What you delivered, shown in the facts panel
cover image path Relative to the JSON file, for example ../../assets/projects/halden-rail/cover.jpg
coverAlt text Describe the image for people who can’t see it
gallery list of { src, alt, caption? } Images in the story. Default none
challenge text The problem, one paragraph
approach list of text, at least one What you did. Each item is one paragraph
outcome text The result, one paragraph
metrics list of { value, label, highlight }, max 4 Numbers that prove the outcome
testimonial { quote, name, role }, optional A client quote
credits list of { role, name } Who worked on it

The schema lives in src/content/schema.ts. If you add a field there, add it to keystatic.config.ts too so the editor offers it.

Pick a layout per project with layout. They share the header, facts, outcome and credits, and differ in how the story and images are told.

Layout How it reads Best for
editorial Text and images alternate down the page, with a scroll-driven hero image Most projects; the default
narrative The story scrolls beside a sticky image that changes with each chapter Process-heavy projects
gallery Large images lead; the challenge and approach stay short in a sticky side column Visual work: identity, photography, print

narrative tells the story in three chapters: challenge, approach and outcome. The sticky frame shows the cover for the challenge, then the first and second gallery images, so give these projects at least two gallery images. On phones, and without JavaScript, each image sits inline above its chapter.

  • The hero features the first project with featured: true, or the first project by order if none is featured.
  • The client marquee lists every project’s client.
  • Selected work lists the first five projects by order, with a link to all of them.
  • Results shows up to four metrics, one per project, taken from metrics marked "highlight": true. Mark at most one metric per project.
  • The quote comes from the Halden Rail case study, or the first project with a testimonial if that one is gone. Change the slug in src/pages/[...lang]/index.astro to pick yours.

The bottom of each case study suggests two others: shared disciplines count most, then the same industry, then order breaks ties. There is nothing to configure.

The lists live in src/content/taxonomy.ts. To add one, add it there and add its label to the taxonomy section of every dictionary (en.ts, de.ts, id.ts); npm run check tells you if one is missing. The work page filters and the CMS dropdowns update by themselves.

Put a case study’s images in src/assets/projects/<slug>/. Astro resizes them at build time and serves WebP in several sizes, so export large (2400px on the long side is plenty) and don’t pre-compress. The CMS does this for you when you upload.

Copy the file into de/ or id/ with the same name and translate the text fields. Images are shared, so leave the paths as they are but add one ../ (the file is one folder deeper). See Translating content.