Personal academic site for Umberto Villa. Jekyll, hosted on GitHub Pages.
The single source of truth is files/uvilla.bib (maintained in JabRef).
Everything on the Publications page is generated from it:
files/uvilla.bib ──scripts/bib2yaml.py──► _data/publications.yml ──Liquid──► publications.html
files/ is published, so the bib is also downloadable at
https://uvilla.github.io/files/uvilla.bib.
On GitHub, this is automatic: .github/workflows/pages.yml regenerates
_data/publications.yml on every push, before Jekyll runs. Edit the .bib,
push, and the publications page updates — nothing else to do.
_data/publications.yml is therefore generated, not committed (it is in
.gitignore). Build it locally before serving:
make pubs # or: python3 scripts/bib2yaml.py
make serve # runs `make pubs` firstmake check parses the bib and reports problems (missing years, missing
journal names, articles with no classification, unrecognised topic slugs,
entries where "Umberto Villa" is not among the authors) without writing
anything.
The script needs only Python 3.8+ — no third-party packages.
- de-duplicates entries that share a DOI, an arXiv id, or a title
- normalizes LaTeX accents to Unicode (
Cram\'er→ Cramér) - collapses co-author name variants to their most complete spelling
(
M. A. Anastasio,Mark Anastasio→Mark A Anastasio) - moves generational suffixes after the family name (
Hormuth, David A. II→David A Hormuth II) - classifies each entry as article / preprint / conference / dataset /
software / thesis, and reads its research topics from
classification
Topics come from the non-standard classification field in the .bib — one
or more comma-separated slugs:
@article{MyCiteKey2026,
...
classification = {pact, dlirm}
}| Slug | Topic |
|---|---|
vi |
Virtual imaging studies |
pact |
Photoacoustic computed tomography |
usct |
Ultrasound computed tomography |
iqa |
Image quality assessment |
dlirm |
Deep learning-based image reconstruction |
ipuq |
Inverse problems, uncertainty quantification, digital twins |
amg |
Preconditioners, multigrid and numerical upscaling |
cfd |
Computational fluid dynamics |
health |
Data science for public health |
This table mirrors TOPICS in scripts/bib2yaml.py, which is the single
place to edit. Adding a slug there (and using it in the .bib) is all that is
needed — the filter chips, the tags on each entry, and their ordering all come
from it.
The order of TOPICS in scripts/bib2yaml.py sets both the order of the
filter chips and the order of tags on each entry; edit it there to add or
rename a topic.
An article with no classification still appears in the list, but only under
the "All" filter — make check reports those, along with any slug that isn't
in the table above.
publications.html currently renders only kind: "article". All other kinds
are already in _data/publications.yml; to show conference papers too, change
the filter near the top of the file:
{% assign articles = site.data.publications.entries | where: "kind", "article" %}Reference lists on the Research page come from the same data, by cite key:
{% include pub_refs.html keys="LiVillaLiEtAl24, ZhouVillaAnastasio23" %}An unknown key renders an HTML comment rather than a broken citation, so a renamed key shows up as a gap.
.github/workflows/pages.yml replaces GitHub's built-in
pages-build-deployment pipeline, so that the bib can be turned into
_data/publications.yml before Jekyll runs:
push ─► bib2yaml.py ─► jekyll-build-pages ─► deploy-pages
One-time setup: Settings → Pages → Build and deployment → Source →
GitHub Actions. While the source is still "Deploy from a branch", the
deploy job fails (the build job still runs and reports).
The run summary for each deploy shows the entry counts per topic, and any
entry needing attention is raised as a job warning — so a missing DOI or
classification is visible from the Actions tab without opening the log.
The generate step passes --min-articles 60. The bib parser is deliberately
lenient, so a truncated or malformed .bib would otherwise produce a nearly
empty publication list and deploy it without complaint; below that floor the
script writes nothing and fails the build instead.
bundle exec jekyll serve # or: make serveThere is no Gemfile; GitHub Pages builds with its own gem set. To build
locally, gem install jekyll (or add a Gemfile with github-pages).
| Path | What it is |
|---|---|
files/uvilla.bib |
the publication source of truth (also served as a download) |
_config.yml |
site metadata, affiliations, profile links |
_data/navigation.yml |
top navigation |
_data/publications.yml |
generated, gitignored — built on every deploy |
.github/workflows/pages.yml |
regenerate → build → deploy |
_layouts/ |
base, default (page + header band), home (hero), wide |
_includes/ |
head, header, footer, pub_refs |
assets/css/style.scss |
the whole design system (plain CSS, no theme gem) |
scripts/bib2yaml.py |
bib → YAML generator |
material/ |
drafts and working notes, excluded from the build |
images/optimized/ |
web-sized images; originals kept alongside |
- No theme gem.
assets/css/style.scssis self-contained plain CSS built on custom properties. - Careful with
clamp(). GitHub Pages compiles this file with Ruby Sass 3.7 (jekyll-sass-converter1.5.2), which predatesclamp()/min()/max()and tries to evaluate arithmetic in function arguments. Writingfont-size: clamp(2rem, 1.35rem + 2.6vw, 3rem)fails the build withIncompatible units: 'vw' and 'rem'. Sass leaves custom property values alone, so every fluid value lives in a--tokenin:rootand is used viavar(). Keep new fluid values in tokens too. (Switching Pages to a custom Actions workflow with your ownGemfilewould let you use modern Dart Sass and drop this constraint.) - The site keeps the filename
assets/css/style.scsson purpose: with notheme:in_config.yml, GitHub Pages falls back tojekyll-theme-primer, and a file at that exact path shadows the theme's stylesheet. - Light and dark themes, following the OS by default with a manual toggle in
the header (stored in
localStorage). - Navigation collapses to a CSS-only hamburger below 900px.
- Page container defaults to a reading measure (
.prose); pages that lay out grids setcontainer: fullin their front matter.