Source code for datatranslator.co.uk, my personal portfolio site, built to showcase data analytics projects and case studies.
- Hugo (v0.163.3, extended): static site generator
- PaperMod: base theme, included as a git submodule
- GitHub Pages: hosting
- GitHub Actions: build and deployment
- Ionos: domain registrar and DNS
.
├── archetypes/
│ └── projects/ # Directory archetype used by `hugo new content projects/<name>`
│ └── index.md
├── layouts/ # Custom layout overrides (theme is never edited directly)
│ ├── _markup/ # render-image.html: responsive image render hook
│ ├── _partials/ # extend_head.html
│ ├── _shortcodes/ # summary.html: renders project_summary as a table
│ └── list.html # Projects landing page
├── assets/css/extended/ # Custom CSS (colours, fonts, figures, tables)
├── content/
│ ├── about.md # About page
│ └── projects/ # _index.md (landing page) + one bundle folder per case study
│ └── <project-name>/
│ ├── index.md # Front matter and write-up
│ └── *.jpg # Images live in the bundle, next to the content
├── static/
│ ├── fonts/ # Self-hosted webfonts
│ ├── img/ # Site-wide images only (headshot, logo)
│ └── favicon*, site.webmanifest
├── hugo.yaml # Site configuration
└── TODO.md # Task backlog
Customisation principle: all styling and layout changes live in layouts/, assets/css/extended/, and hugo.yaml, never inside themes/PaperMod/. This means the theme can be updated (via the submodule) without losing any customisations.
Current layout overrides:
layouts/_markup/render-image.html: turns plain Markdown images into responsive, captioned figures (see below)layouts/_shortcodes/summary.html: renders theproject_summaryfront matter as a semantic tablelayouts/list.html: Projects landing page; shows each project'sproject_summary.goalas its preview text, falling back to Hugo's.Summaryif the field is absent
Clone the repo with submodules included:
git clone --recurse-submodules https://github.com/clarelgibson/data-translator.git
cd data-translatorIf you've already cloned without submodules:
git submodule update --init --recursiveRun the local development server:
hugo server -DSite will be available at http://localhost:1313.
Each project is a Hugo leaf bundle: a folder containing index.md plus any images the write-up uses.
-
Scaffold the bundle:
hugo new content projects/<project-name>
This uses the directory archetype at
archetypes/projects/to createcontent/projects/<project-name>/index.mdwith the front matter skeleton, the summary shortcode and the standard section headings already in place. The folder name becomes the URL slug, so use lowercase words separated by hyphens. -
Fill in the front matter:
titleis pre-populated from the folder name; adjust if needed.cover: add a cover image to the bundle folder and setimage(filename only),altandcaption. The caption supports Markdown links for photo attribution. Covers are handled by the theme, not the render hook: they are resized forsrcsetbut displayed at their native aspect ratio, so crop covers to a wide banner shape (roughly 3:1) before adding them. (A TODO task exists to enforce a standard ratio automatically via acover.htmlpartial override.)project_summary: completegoal,role,tools,dataandlinks(each link is aname/urlpair; remove any that don't apply). These render as the summary table at the top of the page via thesummaryshortcode, andgoaldoubles as the preview text on the Projects landing page, so write it to stand alone.tags: lowercase, as a YAML list.
Why
project_summaryand notsummary? Hugo reservessummaryfor its own.Summarypage variable and strips it from.Params, so a front matter field namedsummarywould silently disappear. Don't rename it. -
Add body images by dropping them into the bundle folder and referencing them with plain Markdown:

The image render hook handles the rest automatically:
- Raster images (JPG, PNG) are resized to a set of responsive widths (480/720/1080/1440px) and served with
srcset, so browsers download only what they need. Images are never upscaled beyond their original width, so body images need no cropping or resizing before adding them; just avoid tiny originals. (Cover images are the exception; see step 2.) - SVGs are resolution-independent, so they skip the resize pipeline and render at their natural size.
- In both cases the image is wrapped in a
<figure>, with the caption (the quoted title text) rendered as a<figcaption>. - Images referenced from outside the bundle (e.g. external URLs) fall back to a plain
<img>with no optimisation, so keep project images in the bundle.
- Raster images (JPG, PNG) are resized to a set of responsive widths (480/720/1080/1440px) and served with
-
Write the content under the scaffolded headings, which follow the standard case study spine. Rename the flexible sixth section (
Outcomein the archetype) to suit the project, e.g. Recommendations, Conclusion or The tool. -
Publish by setting
draft: false. Preview drafts locally withhugo server -Dbefore pushing.
Edit content/about.md directly. The profile photo lives in static/img/.
Edit content/projects/_index.md.
| Element | Value |
|---|---|
| Navy (structural) | #202C39 |
| Light grey (structural) | #ECF0F4 |
| Teal (secondary/meta) | #55868C |
| Coral (hover accent only) | #FF4F5C |
| Amber (reserve) | #EDAE49 |
Fonts are self-hosted in static/fonts/ and declared in assets/css/extended/fonts.css, which is the source of truth for families and weights.
Pushes to main trigger a GitHub Actions workflow that builds the site with Hugo and deploys it to GitHub Pages.
The custom domain (datatranslator.co.uk) is configured at Ionos and pointed at GitHub Pages. DNS, GitHub Pages settings, and the Hugo build are three independent parts of the chain. If something breaks, troubleshoot each separately rather than assuming a single cause.
Content and code are © Clare Gibson unless otherwise credited (see individual case studies for third-party image attributions).