Website Maintenance Guide
Personal academic website built with Quarto.
Live URL: https://lasaibarrenada.github.io/
Source repo: https://github.com/LasaiBarrenada/LasaiBarrenada.github.io/
Quick Reference
| What do you want to update? | File to edit |
|---|---|
| Bio text / photo | index.qmd |
| Add a new project | projects/projects.xlsx |
| Add a conference talk | speaking/conferences.xlsx |
| Add a CV entry (job, education, award…) | cv/cv.qmd |
| Add a publication to the CV PDF | cv/peer_bib.bib |
| Replace the CV PDF | cv/cv.pdf |
| Refresh publications network (new paper) | Delete _freeze/Publications/ then render |
| Replace navbar logo | img/lb_logo_opt1.svg |
| Replace browser tab icon (favicon) | img/favicon.svg |
| Exclude/include a GitHub repo on Software page | Edit EXCLUDE_REPOS in software/software-page.qmd |
| Link a paper to an OSF card on Software page | Add the OSF node ID in the OSF_ID column of projects/projects.xlsx, or add to osf_extra_papers in the R chunk |
| Change navbar links or social icons | _quarto.yml |
| Change global font / colours | html/styles.scss |
| Change landing-page styles | html/landing_page_styles.css |
| Change timeline styles (projects & speaking) | html/timeline.css |
| Change page footer text | _quarto.yml → page-footer |
| Update “last updated” date | _variables.yml |
| Edit the license page | license/index.qmd |
1. How to Rebuild and Deploy
Render the site locally (generates _site/):
quarto renderPreview locally (hot-reload):
quarto previewPublish to GitHub Pages:
quarto publish gh-pagesThis pushes the rendered _site/ to the gh-pages branch automatically.
2. The Freeze Mechanism
execute: freeze: true is set globally in _quarto.yml. This means:
- R code chunks are only executed once. Results are cached in
_freeze/. - On subsequent renders Quarto uses the cached output — it does not re-run R.
- This keeps the build fast and reproducible without needing R dependencies installed on the server.
Exceptions: The Projects and Speaking pages have freeze: false set in their YAML header, so they always re-execute when rendered. This means you can just edit the Excel files and render — no need to delete _freeze/ folders for those pages.
When you MUST delete the freeze cache (to force re-execution on other pages):
| Situation | Cache folder to delete |
|---|---|
| New paper on Google Scholar | _freeze/Publications/ |
| Any other R-code change | The folder matching the changed .qmd |
After deleting, run quarto render and the R code will re-execute and create a new cache.
3. Landing Page — index.qmd
Update bio text
The bio lives inside the {=html} block. Edit the three <p> paragraphs directly:
<p>I'm a final-year PhD student at KU Leuven ...</p>Replace profile photo
- Save the new photo as
img/lasai_headshot.png(overwrite the existing file). - Recommended: square crop, at least 600×600 px.
- No code changes needed — the page references this filename directly.
Interactive regression Easter egg
The hero background canvas has an interactive feature on top of the particle animation:
- A hint (“Click anywhere to place data points”) appears in the top-right in site-blue (
#007BFF) at opacity 0.65, fading very slowly. - Clicking on the background (not on links/buttons) places a blue dot with a white border and a pop-in animation. A counter (“3 / 10”) tracks progress.
- At 10 dots, a LOESS curve (span = 0.75, tricube kernel) is fitted through the points and drawn as a solid blue line. The R² value and the message “This is basically my job :)” appear in the top-right corner of the canvas.
- A “⟳ Random data” button appears just below the tagline (top-right,
top: 56px). Clicking it clears any existing dots, generates 10 new random points spread across the canvas, and immediately fits + displays the LOESS and R². Clicking it again regenerates a fresh random set. - Clicking anywhere on the canvas (11th click, or first click after a random set) clears everything and the cycle restarts.
Key parameters in the <script> block of index.qmd: - MAX_DOTS — number of points before the curve is drawn (default: 10) - 0.75 in computeLoess() — the LOESS span (smoothing parameter) - hintOpacity — initial opacity of the hint text (default: 0.65) - top: 56px in #random-btn CSS — positions the button below the “This is basically my job :)” line
Resizing the browser window clears dots and resets the hint.
4. Projects Page — projects/projects-page.qmd
The page is a vertical HTML timeline generated by an R chunk (tl-projects) that reads projects/projects.xlsx. This works the same way as the speaking page — the R code builds the HTML cards from the spreadsheet data.
Both pages share the same timeline CSS from html/timeline.css (see §10). Year dividers are automatically inserted between entries from different years to visually separate them.
Because the R output is inserted into Markdown before Pandoc processes it, no line of generated HTML may start with 4+ spaces (same constraint as the speaking page). Keep all sprintf() / paste0() templates at ≤ 3-space indentation.
Add a new project
Open projects/projects.xlsx and add a new row. The columns are:
| Column | Description | Example |
|---|---|---|
Title |
Paper/project title | Clustered Flexible Calibration… |
Year |
Four-digit year | 2026 |
Month |
Numeric month (used for sorting) | 3 |
Venue |
Journal name or “arXiv preprint” | BMJ Open |
Link_1_name |
Label for the first link | Paper |
Link_1 |
URL for the first link | https://… |
Link_2_name |
Label for the second link | Code |
Link_2 |
URL for the second link | https://… |
Link_3_name |
Label for an optional third link (leave blank if none) | Package |
Link_3 |
URL for the optional third link | https://… |
Summary |
2–3 sentence plain-language summary | Two or three sentences… |
If Link_1 is left blank, the project is displayed as Upcoming — grey hollow dot, grey card, and an “Upcoming” badge. Once published, fill in the links and the project switches to the normal blue style.
Award links
If any Link_X_name contains the word “Award” (case-insensitive), the link button is automatically rendered in a gold award style with a 🏆 icon. This is handled by the R code in projects-page.qmd and styled via .tl-award in html/timeline.css.
Freeze is disabled on this page, so just edit the Excel and re-render — no need to delete cache folders.
5. Talks Page — speaking/speaking-page.qmd
The page appears as “Talks” in the navbar (renamed from “Speaking”). The page has two sections:
- Timeline — an R chunk (
tl-speaking) reads the Excel, builds HTML cards, and outputs them viacat()withresults='asis'. Shares timeline CSS with the projects page viahtml/timeline.css. Year dividers are automatically inserted between entries from different years. - Map — an R chunk (
map-data) geocodes conference cities and injects the coordinates as JSON for the Leaflet interactive map.
The map uses Leaflet.js with CartoDB Voyager tiles. Conference locations are shown as red circle markers and affiliated institutions (KU Leuven, UMC Utrecht, Memorial Sloan Kettering) as blue circle markers with pulsing rings. Clicking any marker opens a styled popup. The map works on both desktop and mobile with native touch support. Zoom is constrained (minZoom: 2, maxBounds) so only a single world view is shown.
Map → timeline linking
Each timeline entry has a unique id attribute (e.g., talk-iscb-2024), generated by the R code from Code_Name and Year. Conference marker popups include a “Show in timeline ↓” link. Clicking it smooth-scrolls to the matching timeline entry and applies a 2-second blue pulse highlight (tl-highlight class → tl-flash animation in html/timeline.css). The ID is computed identically in both R and JS: talk-{code_name}-{year}, lowercased with non-alphanumeric characters replaced by hyphens.
University popup labels
UMC Utrecht and Memorial Sloan Kettering popups show “Visiting Researcher” below the institution name. KU Leuven does not (it’s the home institution). This is handled in the JS universityData.forEach block with a conditional on d.name.
Because the R output is inserted into Markdown before Pandoc processes it, no line of generated HTML may start with 4+ spaces — Pandoc would treat it as a code block and escape the tags. Keep all sprintf() / paste0() templates at ≤ 3-space indentation.
Add a new talk or poster
Open speaking/conferences.xlsx and add a new row. The columns are:
| Column | Description | Example |
|---|---|---|
Conference |
Full conference name | International Society for Clinical Biostatistics |
Code_Name |
Short acronym shown on the timeline | ISCB |
Year |
Four-digit year | 2026 |
Month |
Numeric month (used for sorting) | 7 |
Location |
City name (used for geocoding the map) | Geneva |
Title |
Full talk/poster title | Multicenter calibration… |
Type |
Oral or Poster (leave blank for upcoming) |
Oral |
Link_1_name |
Label for the first link button | Slides |
Link_1 |
URL for the first link | https://… |
Link_2_name |
Label for an optional second link (leave blank if none) | Paper |
Link_2 |
URL for the optional second link | https://… |
Add or change an affiliated institution on the map
The list of institutions is defined in the map-data R chunk inside speaking-page.qmd:
uni_coords <- data.frame(
name = c("KU Leuven", "UMC Utrecht", "Memorial Sloan Kettering"),
city = c("Leuven", "Utrecht", "New York")
)Add or remove rows as needed. The geocoding will resolve city names automatically.
Freeze is disabled on this page, so just edit the Excel and re-render — no need to delete cache folders. Note that the map geocoding will re-run each time, which requires an internet connection.
6. Publications Page — Publications/publications-page.qmd
The page pulls data live from Google Scholar (user ID uPOL3NkAAAAJ) using the scholar R package. The co-authorship network is built automatically from your publication list.
When a new paper appears on Google Scholar
- Delete
_freeze/Publications/. - Run
quarto render. - The R code will re-fetch publications, re-run the deduplication pipeline, and rebuild the network.
Publication deduplication (preprint + journal)
The pipeline also detects preprint/working-paper duplicates by comparing titles with Jaccard similarity. If a preprint you have on Google Scholar gets published and the two entries persist simultaneously, the preprint will be automatically dropped when Jaccard similarity ≥ 0.28 with the published version. No manual action needed.
Visual enhancements (D3)
The network uses several visual techniques implemented in the <script> block:
- Focus + dim: Hovering or clicking a node highlights it and its connected links/nodes; everything else fades to 12% opacity (
DIM_OPACITY). When a node is selected (clicked), hover on other nodes does not change the focus — only clicking a new node or the background changes it. - Gradient nodes: Each node uses an SVG radial gradient (lighter centre, darker edge) defined in
<defs>. The centre node also has a glow filter (feGaussianBlur+feMerge); coauthor nodes have a drop shadow (feDropShadow). - Curved links: Links are
<path>elements with quadratic Bézier curves instead of straight<line>elements. Adjacent links alternate curve direction (sign = i % 2) so they don’t overlap. - Entrance animation: Nodes start at
r = 0and scale in withd3.easeBackOut(slight overshoot); links and labels fade in. The animation fires 400 ms after page load to let the simulation settle first.
7. CV
The CV exists in two forms:
| File | Purpose |
|---|---|
cv/cv-page.qmd |
HTML page shown on the website — renders the CV content directly with styled sections, a table of contents, and a “Download PDF” button at the top |
cv/cv.qmd |
PDF source using quarto-cv-pdf format — generates cv.pdf for download |
Both files should be kept in sync when you add new entries.
Add a new entry (job, talk, award, etc.)
Update both files:
cv-page.qmd— Add an entry using the Quarto div pattern:
:::: {.cv-entry}
::: {.cv-entry-title}
Institution Name
:::
::: {.cv-entry-date}
MM/YYYY – MM/YYYY
:::
::::
::: {.cv-entry-detail}
Role description, City
:::cv/cv.qmd— Add the corresponding entry using LaTeX\hfillfor dates:
**Institution Name**
Role description (City) \hfill MM/YYYY–MM/YYYYThen re-render cv/cv.qmd to regenerate the PDF.
Add a publication to the CV
Publications on the HTML page are rendered automatically from cv/peer_bib.bib using Quarto’s bibliography support (nocite: '@*'). Add the BibTeX entry to the bib file and it will appear on both the HTML page and in the PDF.
BibTeX entry format example:
@article{barrenada2025example,
author = {Barreñada, Lasai and Van Calster, Ben},
title = {Title of the paper},
journal = {Journal Name},
year = {2025},
volume = {10},
pages = {123--130},
doi = {10.xxxx/xxxxx}
}Replace the CV PDF only
Overwrite cv/cv.pdf with the new file. The HTML page links to this file via the download button.
Technical notes
cv-page.qmdusesengine: knitr(needed for the inline R expressionSys.Date()). Without it, Quarto falls back to Jupyter/Python and fails.cv-page.qmdhasfreeze: truesince publications from the bib file don’t change often. Delete_freeze/cv/cv-page/when you add a new publication to force re-rendering.
8. Software Page — software/software-page.qmd
The page fetches data from two platforms using client-side JS — no R code, no freeze cache:
- GitHub (username
LasaiBarrenada) — public repositories displayed in a card grid. - OSF (user ID
pgkjv) — public top-level projects displayed in a separate card grid below.
How it works — GitHub
A <script> block calls https://api.github.com/users/LasaiBarrenada/repos?type=owner&sort=pushed&per_page=100 on page load. Repos are displayed in a responsive 2-column card grid (1 column on mobile). Each card shows the repo name (linked), description, primary language with a coloured dot, star count, fork badge (if applicable), topic tags, and a relative “Updated X ago” timestamp.
Configuration — GitHub (inside the first <script> block)
| Variable | Purpose |
|---|---|
USERNAME |
GitHub username to fetch repos from (currently LasaiBarrenada) |
EXCLUDE_REPOS |
Array of repo names to hide — currently LasaiBarrenada.github.io (website), LasaiBarrenada (profile config), web_resources (web assets) |
CONTRIB_REPOS |
Array of owner/repo strings for external repos the user contributes to — fetched individually and shown with a “Contributor” badge |
LANG_COLORS |
Map of language names → hex colours for the dot indicator |
How it works — OSF
The OSF API does not support CORS, so OSF data is fetched at build time by an R chunk (osf-projects), not in the browser. The R code calls https://api.osf.io/v2/users/pgkjv/nodes/ using jsonlite::fromJSON(), filters to top-level public projects (where root.data.id == node.id), and generates HTML cards via cat() with results='asis'. The page has freeze: false so OSF data refreshes on every render.
Cards show the project title (linked to OSF), an “OSF” badge (dark blue #214370), category label, description, tags, related paper links (if any), child components (if any), and an absolute date (“Updated Mar 2026”). Cards are sorted by most recently modified.
Child components on OSF cards
Each OSF card fetches its first-level child components via /nodes/{id}/children/ at render time (one API call per top-level project). Children are shown in a shaded box labelled “Components” with a left navy border. Each child is a linked item showing an emoji icon (by category), the component title, and a small category label. All child categories are shown.
Configuration — OSF (inside the R chunk osf-projects)
| Variable | Purpose |
|---|---|
| OSF user ID | Hardcoded in the API URL (pgkjv) — change there if needed |
cat_labels |
Named vector mapping OSF category slugs → friendly display labels |
osf_extra_papers |
Named list of extra paper links for OSF projects not in projects.xlsx |
truncate_title() |
Truncates paper titles to 58 characters + “…” on OSF cards (full title shown on hover via title attribute) |
Styling
Card grid styles are in html/software.css. The page loads this file with a <link> tag. Do not inline styles in the page file. OSF-specific styles (.sw-osf-badge, .sw-osf-category, .sw-topic-osf, .sw-card-osf:hover, .sw-section-heading, .sw-papers, .sw-paper-link) are also in software.css.
Gotchas
- The GitHub public API allows 60 requests/hour without authentication. This is sufficient for a personal site but will fail if the page is loaded very frequently in a short time. If rate-limited, a fallback message with a link to the GitHub profile is shown.
- The OSF public API has no documented unauthenticated rate limit, but is similarly generous. If it fails, a fallback message with a link to the OSF profile is shown.
- Archived repos are automatically excluded (GitHub). Forked repos are filtered out from the main fetch.
- To showcase external repos the user contributes to (via commits/PRs), add the upstream
owner/repopath to theCONTRIB_REPOSarray — these are fetched individually and shown with a “Contributor” badge. - Each entry in
CONTRIB_REPOSuses one extra API call, so keep the list short to stay within the 60 req/hr limit.
9. Site-wide Settings — _quarto.yml
Search is disabled (search: false). The website title is set to "Lasai Barreñada" (short form — the full “Medical Statistician & Researcher” description is in description-meta for SEO only). The navbar tab for talks is labelled “Talks” (the page file remains in the speaking/ folder).
Change your name or description (appears in browser tab / SEO)
title-meta: "Your Name"
description-meta: "Your description"10. Global Styles
| File | Controls |
|---|---|
html/styles.scss |
Global font (Inter for body and headings), link colours, spacing, navbar responsive sizing — applied to all pages |
html/timeline.css |
Shared timeline styles for Projects and Speaking pages (cards, dots, dates, links, badges, award styling, expand/collapse, mobile breakpoints) |
html/landing_page_styles.css |
Hero section layout on the landing page |
html/seo.html |
Meta tags injected into every page <head> |
11. Images and Branding
| File | Purpose |
|---|---|
img/lasai_headshot.png |
Profile photo on the landing page |
img/lb_logo_opt1.svg |
Navbar logo — “LB” initials with R prompt > and sigmoid curve. Referenced in _quarto.yml → navbar: logo |
img/favicon.svg |
Browser tab icon — blue rounded square with “LB”. Referenced in _quarto.yml → favicon |
img/website_thumbnail.png |
Social preview image (Twitter/Open Graph) |
img/logo_kuleuven.svg |
KU Leuven logo (spare, not currently used on site) |
img/logo_umcutrecht.svg |
UMC Utrecht logo (spare, not currently used on site) |
img/logo_msk.jpeg |
Memorial Sloan Kettering logo (spare, not currently used on site) |
projects/*.png |
Project thumbnail images referenced in the projects page |
Replace the favicon
Overwrite img/favicon.svg (or create a new file and update the favicon: path in _quarto.yml). Keep it simple — favicons display at 16–32px.
12. License Page — license/index.qmd
The license page (linked in the footer) contains:
- An MIT License covering the website source code.
- A copyright notice for written content, publications, talks, and images.
- An acknowledgement of the original template by Marvin Schmitt.
Edit license/index.qmd directly. No R code, no freeze cache.
13. Variables File — _variables.yml
Small global variables used across the site (e.g. the “last updated” date in the footer). Edit directly:
last_updated: "February 2026"