Implements cookie consent using vanilla-cookieconsent v3 to comply with
GDPR requirements. Analytics scripts are blocked until user consent is
obtained.
## Changes
**Library Integration**
- Added `vanilla-cookieconsent@3.1.0` to `_config.yml` third-party
libraries with SRI hashes
- Created `_scripts/cookie-consent-setup.js` with consent modal and
settings configuration
- Added CSS/JS includes in `_includes/head.liquid` and
`_includes/scripts.liquid`
**Analytics Blocking**
- Modified analytics scripts in `scripts.liquid` and
`distill_scripts.liquid` to use conditional `type="text/plain"
data-category="analytics"` when consent is disabled
- Blocks Google Analytics, Cronitor, Pirsch, and OpenPanel until consent
granted
- Library automatically converts blocked scripts to executable on user
acceptance
**Configuration**
- Added `enable_cookie_consent` flag (default: `false`)
- Cookie categories: `necessary` (always on), `analytics` (optional)
- 182-day cookie expiration, auto-clear on rejection
## Implementation
```liquid
{% if site.enable_cookie_consent %}
<script type="text/plain" data-category="analytics" async src="...gtag.js"></script>
{% else %}
<script async src="...gtag.js"></script>
{% endif %}
```
Enable in `_config.yml`:
```yaml
enable_cookie_consent: true
```
## Screenshots
**Consent Modal**

**Settings Modal**

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `cdn.jsdelivr.net`
> - Triggering command: `/usr/bin/curl curl -s REDACTED` (dns block)
> - `cookieconsent.orestbida.com`
> - Triggering command:
`/home/REDACTED/work/_temp/ghcca-node/node/bin/node
/home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps
/home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js`
(dns block)
> - `medium.com`
> - Triggering command: `/usr/local/bin/ruby ruby
/usr/local/bundle/bin/bundle exec jekyll serve --watch --port=8080
--host=0.0.0.0 --livereload --verbose --trace --force_polling s
6_64-linux /etc/locale.gen by/backward -binutils_2.44-3grep .deb eb
/systemd-sysctl` (dns block)
> - `publish.twitter.com`
> - Triggering command: `/usr/local/bin/ruby ruby
/usr/local/bundle/bin/bundle exec jekyll serve --watch --port=8080
--host=0.0.0.0 --livereload --verbose --trace --force_polling lude
lude/ruby-4.0.0//run/containerd/io.containerd.runtime.v2.task/moby/a4760212ce4eda97e32b6dfa4d3ea/usr/sbin/iptables
r/runc-log.json f8_range lude r-strong E r/wc7in709a54d7hshow` (dns
block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/alshedivat/al-folio/settings/copilot/coding_agent)
(admins only)
>
> </details>
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>GDPR compliant cookie consent dialog</issue_title>
> <issue_description>Hi,
>
> since I am from Europe and want my site to be accessed by EU citizens
I need to ask users if they are ok with the use of cookies. Adding a
dialogue by default that that asks the user before any cookies /
localStorage are used would be nice.
>
> I have integrated the dialog from
[https://www.cookieconsent.com/](https://www.cookieconsent.com/) on [my
website](http://hschwane.github.io) and am happy to file a PR if you are
interested. However It is not shown directly at the top, but below the
menu bar for some reason. Not sure how to fix that.
> </issue_description>
>
> <agent_instructions>Implement this using cookie consent dialog from
https://github.com/orestbida/cookieconsent. The documentation is
available at
https://cookieconsent.orestbida.com/essential/getting-started.html.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> <comment_new><author>@alshedivat</author><body>
> @hschwane, thanks for pointing this out. It's would be great if you
could send a PR.
>
> To answer your question about positioning, the menu bar has the
absolute position at the top of the page and keeps moving with
scrolling. You can change that behavior in `_config.yml` by setting
`navbar_fixed: false` (see [line
27](https://github.com/alshedivat/al-folio/blob/master/_config.yml#L27)).
If you send a PR with basic functionality, I can help with adjusting the
styles a bit. Thanks!</body></comment_new>
> <comment_new><author>@alshedivat</author><body>
> Just catching up on this discussion.
>
> @hschwane, thanks for providing an implementation based on
`ihavecookies`! I looked through alshedivat/al-folio#223. It seems like
to be able to correctly (un)block different types of cookies (all of
which are third-party cookies) requires adding quite a bit of extra
JavaScript (including adding it directly to posts or pages in some
cases, e.g., when posts embed youtube videos or other external content).
This approach will put some extra burden on contributors and maintainers
of al-folio down the line.
>
> By the way, the dialog from https://www.cookieconsent.com/ is similar
in that sense as it requires to manually add ALL scripts that use
cookies. So, if someone contributes a new feature to al-folio that uses
cookies, the contributor would have to go and update the cookie consent
dialog as well (or edit quite a bit of JS if `ihavecookies` is used).
>
> Thinking about this more, I currently see only three options:
> 1. If we want to have cookie consent as a core part of al-folio (which
can be easily enabled or disabled), it needs to be a service that
automatically identifies and blocks cookies, without putting an extra
burden on the contributors or maintainers. The hosted version of [Cookie
Consent by Osano](https://www.osano.com/features/consent-management) is
an example.
> 2. Another option is to provide a very minimalistic cookie consent
dialog which simply says that the website uses cookies, without an
option to opt-in or opt-out (e.g., see the open-source edition of
[Osano](https://www.osano.com/cookieconsent/download/)).
> 3. Finally, not have cookie consent as part of al-folio, and make the
user responsible for figuring out whether they need a consent dialog and
which service to use.
>
> Let me know what you think.</body></comment_new>
> <comment_new><author>@alshedivat</author><body>
> the best way to add GDPR compliant cookie consent is using
https://github.com/orestbida/cookieconsent</body></comment_new>
> </comments>
>
</details>
> **Custom agent used: customization_agent**
> Expert customization assistant for the al-folio Jekyll academic
website template
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixes alshedivat/al-folio#199
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/alshedivat/al-folio/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Signed-off-by: George Araújo <george.gcac@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: george-gca <31376482+george-gca@users.noreply.github.com>
Co-authored-by: George Araújo <george.gcac@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
69 KiB
Customize
Here we will give you some tips on how to customize the website. One important thing to note is that ALL the changes you make should be done on the main branch of your repository. The gh-pages branch is automatically overwritten every time you make a change to the main branch.
Note for users without coding experience: You do not need to understand the technology stack or have any coding background to create and customize your own website with al-folio. This template was specifically designed to be accessible to academics and researchers from all backgrounds. You can create a fully functional website by simply editing configuration files and adding content in Markdown, no coding required.
- Customize
- Project structure
- Configuration
- GitHub Copilot Customization Agent
- Understanding the Codebase with Code Wiki and DeepWiki
- Technology Stack
- Modifying the CV information
- Modifying the user and repository information
- Creating new pages
- Creating new blog posts
- Creating new projects
- Adding some news
- Adding Collections
- Adding a new publication
- Changing theme color
- Customizing layout and UI
- Adding social media information
- Adding a newsletter
- Configuring search features
- Social media previews
- Related posts
- Managing publication display
- Adding a Google Calendar
- Updating third-party libraries
- Removing content
- Adding Token for Lighthouse Badger
- Customizing fonts, spacing, and more
- Scheduled Posts
- GDPR Cookie Consent Dialog
- Setting up a Personal Access Token (PAT) for Google Scholar Citation Updates
Project structure
The project is structured as follows, focusing on the main components that you will need to modify:
.
├── 📂 assets/: contains the assets that are displayed in the website
│ └── 📂 json/
│ └── 📄 resume.json: CV in JSON format (https://jsonresume.org/)
├── 📂 _bibliography/
│ └── 📄 papers.bib: bibliography in BibTeX format
├── 📂 _books/: contains the bookshelf pages
├── 📄 _config.yml: the configuration file of the template
├── 📂 _data/: contains some of the data used in the template
│ ├── 📄 cv.yml: CV in YAML format, used when assets/json/resume.json is not found
│ ├── 📄 repositories.yml: users and repositories info in YAML format
│ └── 📄 socials.yml: your social media and contact info in YAML format
├── 📂 _includes/: contains code parts that are included in the main HTML file
│ └── 📄 news.liquid: defines the news section layout in the about page
├── 📂 _layouts/: contains the layouts to choose from in the frontmatter of the Markdown files
├── 📂 _news/: the news that will appear in the news section in the about page
├── 📂 _pages/: contains the pages of the website
| └── 📄 404.md: 404 page (page not found)
├── 📂 _posts/: contains the blog posts
├── 📂 _projects/: contains the projects
└── 📂 _sass/: contains the SASS files that define the style of the website
├── 📂 font-awesome/: contains the SCSS files for Font Awesome
├── 📄 _blog.scss: blog post, tags, and pagination styles
├── 📄 _components.scss: reusable component styles (cards, profiles, CV, projects)
├── 📄 _cv.scss: style of the CV page
├── 📄 _distill.scss: style of the Distill articles
├── 📄 _footer.scss: footer styles
├── 📄 _layout.scss: overall layout styles
├── 📄 _navbar.scss: navigation bar and dropdown menu styles
├── 📄 _publications.scss: publication list and bibliography styles
├── 📄 _tabs.scss: tabbed content styles
├── 📄 _teachings.scss: course and teaching styles
├── 📄 _themes.scss: theme colors and icons
├── 📄 _typograms.scss: typogram diagram styles
├── 📄 _typography.scss: text, headings, links, tables, and blockquote styles
├── 📄 _utilities.scss: utility styles (code highlighting, forms, modals, animations)
└── 📄 _variables.scss: variables used in the SASS files
Configuration
The configuration file _config.yml contains the main configuration of the website. Most of the settings is self-explanatory and we also tried to add as much comments as possible. If you have any questions, please check if it was not already answered in the FAQ.
Note that the
urlandbaseurlsettings are used to generate the links of the website, as explained in the install instructions.
All changes made to this file are only visible after you rebuild the website. That means that you need to run bundle exec jekyll serve again if you are running the website locally or push your changes to GitHub if you are using GitHub Pages. All other changes are visible immediately, you only need to refresh the page.
If changes don't appear after refreshing, try:
- Hard refresh to reload the page ignoring cached content:
- Clear your browser cache completely
- Use a private/incognito session to ensure no cached content:
GitHub Copilot Customization Agent
This repository includes a specialized GitHub Copilot agent (.github/agents/customize.agent.md) designed to help you customize your al-folio website. The agent acts as an expert assistant that can:
- Guide you through common customization tasks step-by-step
- Modify configuration files, add content, and update your website
- Explain technical concepts in plain language (especially helpful if you're not familiar with Jekyll or web development)
- Apply changes directly to your repository files
- Answer questions about how to customize specific features
What the Agent Can Help With
The customization agent can assist with tasks such as:
- Changing basic site information (title, author name, contact details)
- Updating your CV or resume
- Adding and managing publications from BibTeX files
- Creating blog posts, projects, and news items
- Customizing theme colors and styling
- Managing social media links
- Enabling or disabling features in
_config.yml - Adding profile pictures and other assets
- Troubleshooting configuration issues
How to Use the Agent
To use the customization agent:
- Ensure you have a GitHub Copilot subscription
- Open your repository in an editor with GitHub Copilot support (such as VS Code with the GitHub Copilot extension)
- Interact with GitHub Copilot and ask questions or request changes. For more information, check Using custom agents in your IDE
- The agent will guide you through the customization process and can make changes directly to your files
For example, you can ask:
- "How do I change my website's theme color to blue?"
- "Help me add a new blog post about my research"
- "Update my profile information with my new university email"
- "How do I add a publication to my website?"
The agent is designed to be patient and helpful, explaining each step clearly so you understand what's being changed and why.
Important: Verify Agent Output
The customization agent can make mistakes or produce incorrect information. Always review and verify the agent's suggestions and changes before applying them to your repository:
- Review all changes – Before applying any modifications, carefully read what the agent suggests and ensure it makes sense for your needs
- Test locally first – Before pushing changes to GitHub, test them locally using Docker or native setup (see the Installation instructions)
- Check syntax – Make sure any YAML, Markdown, or BibTeX files have correct syntax. Incorrect syntax can break your website
- Verify configuration – If the agent modifies
_config.ymlor other configuration files, check that the changes align with your intentions - Preview on your site – Run your site locally and navigate through it to ensure everything displays correctly and works as expected
- Don't blindly apply changes – Understand what's being changed and why before committing to your repository
Example scenarios where verification is important:
- If the agent suggests a BibTeX entry, verify the syntax matches existing entries in your
_bibliography/papers.bibfile - If the agent modifies your
_config.yml, check that indentation is correct (YAML is very sensitive to spacing) - If the agent creates a new blog post or page, verify the frontmatter (the metadata at the top) is correct
- If the agent suggests changes to theme colors or styling, preview your site locally to ensure the changes look as intended
Note: The customization agent requires GitHub Copilot to be enabled. For more information about GitHub Copilot and its features, see the GitHub Copilot documentation.
Understanding the Codebase with Code Wiki and DeepWiki
If you're interested in learning more about how al-folio works under the hood, or want to understand specific aspects of the codebase for deeper customization, you can use Code Wiki and DeepWiki as supplementary resources.
What are these tools?
Code Wiki and DeepWiki are AI-powered tools that help you explore and understand GitHub repositories through interactive documentation:
-
Code Wiki (powered by Google Gemini) generates interactive documentation from the repository code. You can browse the project structure, search for specific functions or modules, view architecture diagrams, and understand how different components interact.
-
DeepWiki provides an AI chat interface where you can ask natural language questions about the codebase, similar to having an engineer available 24/7. You can ask how features work, search for code patterns, or get explanations of complex logic.
When to use them
Use Code Wiki and DeepWiki only after:
- You have reviewed the relevant sections in this
CUSTOMIZE.mdfile - You have checked the project structure section above
- You have explored the main documentation files (README.md, INSTALL.md, FAQ.md)
- You have checked the GitHub Discussions Q&A section
These tools are best used for:
- Understanding the architecture and how different parts of the codebase work together
- Finding where specific functionality is implemented
- Learning about the Jekyll template structure and Liquid syntax used in the theme
- Exploring how specific features are implemented (e.g., how publications are rendered, how search works, etc.)
Access these tools:
- Code Wiki: Code Wiki for al-folio
- DeepWiki: DeepWiki for al-folio
Technology Stack
Understanding al-folio's technology stack will help you better customize and extend the theme. This section provides an overview of the key technologies and frameworks used in the project.
Frontend
- Markdown: Content is written in Markdown format for pages, blog posts, and collections. This makes it easy to create and maintain content without worrying about HTML.
- Liquid templating: Liquid is used for dynamic template generation. Liquid templates are used in the
_layouts/and_includes/directories to define how your content should be displayed. - HTML & CSS: The theme uses semantic HTML5 and modern CSS for styling and layout.
- SCSS: Stylesheets are written in SCSS (Sass), a CSS preprocessor that provides variables, mixins, and functions for more maintainable styling. SCSS files are located in
_sass/and compiled to CSS during the build process. - Bootstrap: Bootstrap 4.6 is used for responsive grid layout and base styling components.
- JavaScript: Minimal JavaScript is used for interactive features like the dark mode toggle, search functionality, and dynamic content rendering.
- MathJax: For rendering mathematical equations in LaTeX format on your pages and blog posts.
- Mermaid: For creating diagrams (flowcharts, sequence diagrams, etc.) directly in Markdown.
- Font Awesome, Academicons, and Scholar Icons: Icon libraries used throughout the theme for visual elements.
Backend
-
Jekyll 4.x: Jekyll is a static site generator written in Ruby that transforms your Markdown files and templates into a static website. Jekyll is used to:
- Convert Markdown files to HTML
- Process Liquid templates
- Manage collections (posts, projects, news, books, etc.)
- Generate archives and pagination
- Minify CSS and JavaScript
-
Ruby Gems (Jekyll plugins): The project uses several Ruby plugins to extend Jekyll's functionality:
classifier-reborn: Used for categorizing and finding related blog postsjekyll-archives-v2: Creates archive pages for posts and collections organized by category, tag, or datejekyll-feed: Generates an Atom (RSS-like) feed for your contentjekyll-jupyter-notebook: Integrates Jupyter notebooks into your sitejekyll-minifier: Minifies HTML, CSS, and JavaScript for better performancejekyll-paginate-v2: Handles pagination for blog posts and archivesjekyll-scholar: Manages bibliography files (BibTeX) and generates publication pages with citationsjekyll-tabs: Adds tabbed content supportjekyll-toc: Automatically generates table of contents for pages with headersjemoji: Converts emoji shortcodes to emoji images- Other utilities:
jekyll-link-attributes,jekyll-imagemagick,jekyll-twitter-plugin,jekyll-get-json, and more
-
Python: Used for utility scripts like citation updates via Google Scholar (located in
bin/)
Build and Deployment
-
GitHub Actions: Automated workflows for building, testing, and deploying your site. Workflows are defined in
.github/workflows/:- Deploy: Automatically builds and deploys your site to GitHub Pages when you push changes to the main branch
- Link checking: Validates that all links in your site are not broken
- Code formatting: Ensures code follows the Prettier code style
- Accessibility testing: Checks for accessibility issues using Axe
- Lighthouse: Measures site performance and best practices
- Citation updates: Automatically fetches citation counts from Google Scholar
-
GitHub Pages: Free hosting for your static website built by Jekyll
-
Docker: Optional containerization for local development (provides a consistent environment across different machines)
-
Prettier: Code formatter for Markdown, YAML, and Liquid files to maintain consistent formatting
Key Integration Points
Understanding how these technologies work together will help you customize al-folio effectively:
- Content Creation: Write content in Markdown
- Template Processing: Jekyll processes Markdown through Liquid templates
- Styling: SCSS files are compiled to CSS, with Bootstrap providing the responsive layout framework
- Bibliography: BibTeX files are processed by jekyll-scholar to generate publication pages
- Static Site Generation: Jekyll builds all files into static HTML
- Deployment: GitHub Actions automatically deploys the built site to GitHub Pages
Modifying the CV information
Your CV can be created using one of two formats. Choose the format that works best for you, or use both simultaneously by switching between them:
RenderCV Format (Recommended)
_data/cv.yml uses the RenderCV YAML format, which is human-readable and designed specifically for generating professional resumes. This format also enables optional automatic PDF generation via GitHub Actions.
If you choose this format:
- Edit your CV data in
_data/cv.yml - Optionally customize how the PDF is styled by editing:
assets/rendercv/design.yaml— Design and stylingassets/rendercv/locale.yaml— Localization and formattingassets/rendercv/settings.yaml— RenderCV settings
- To display only this format, delete
assets/json/resume.json(optional)
JSONResume Format
assets/json/resume.json uses the JSONResume standard format, which is compatible with other tools and services.
If you choose this format:
- Edit your CV data in
assets/json/resume.json - To display only this format, delete
_data/cv.yml(optional)
Using Both Formats Simultaneously
You can keep both _data/cv.yml and assets/json/resume.json in your repository and switch between them on your website by setting the cv_format frontmatter variable in _pages/cv.md:
---
layout: cv
cv_format: rendercv # options: rendercv or jsonresume
---
Change rendercv to jsonresume to display the JSONResume format instead.
Automatic PDF Generation (RenderCV only)
If you use the RenderCV format, a GitHub Actions workflow can automatically generate a PDF version of your CV whenever you push changes to _data/cv.yml. The PDF is saved to assets/rendercv/rendercv_output/.
To link the auto-generated PDF to your CV page:
Set the cv_pdf variable in the frontmatter of _pages/cv.md to point to the generated PDF:
---
layout: cv
cv_pdf: /assets/rendercv/rendercv_output/CV.pdf
cv_format: rendercv
---
This will add a download button on your CV page that links to the PDF. (The exact filename depends on your RenderCV settings—check the output directory after the first workflow run to see the generated PDF name.)
To disable automatic PDF generation:
Delete or comment out the .github/workflows/render-cv.yml workflow file.
Modifying the user and repository information
The user and repository information is defined in _data/repositories.yml. You can add as many users and repositories as you want. Both informations are used in the repositories section.
Configuring external service URLs
The repository page uses external services to display GitHub statistics and trophies. By default, these are:
github-readme-stats.vercel.appfor user stats and repository cardsgithub-profile-trophy.vercel.appfor GitHub profile trophies
Important: These default services are hosted by third parties and may not be available 100% of the time. For better reliability, privacy, and customization, you can self-host these services and configure your website to use your own instances.
To use your own instances of these services, configure the URLs in _config.yml:
external_services:
github_readme_stats_url: https://github-readme-stats.vercel.app
github_profile_trophy_url: https://github-profile-trophy.vercel.app
To self-host these services, follow the deployment instructions in their respective repositories:
Once deployed, update the URLs above to point to your custom deployment.
Creating new pages
You can create new pages by adding new Markdown files in the _pages directory. The easiest way to do this is to copy an existing page and modify it. You can choose the layout of the page by changing the layout attribute in the frontmatter of the Markdown file, and also the path to access it by changing the permalink attribute. You can also add new layouts in the _layouts directory if you feel the need for it.
Creating new blog posts
To create a new blog post, you can add a new Markdown file in the _posts directory, which is the default location for posts in Jekyll. The name of the file must follow the format YYYY-MM-DD-title.md. The easiest way to do this is to copy an existing blog post and modify it. Note that some blog posts have optional fields in the frontmatter that are used to enable specific behaviors or functions.
If you want to create blog posts that are not ready to be published, but you want to track it with git, you can create a _drafts directory and store them there.
Note that posts is also a collection, but it is a default collection created automatically by Jekyll. To access the posts, you can use the site.posts variable in your templates.
Creating new projects
You can create new projects by adding new Markdown files in the _projects directory. The easiest way to do this is to copy an existing project and modify it.
Adding some news
You can add news in the about page by adding new Markdown files in the _news directory. There are currently two types of news: inline news and news with a link. News with a link take you to a new page while inline news are displayed directly in the about page. The easiest way to create yours is to copy an existing news and modify it.
Adding Collections
This Jekyll theme implements collections to let you break up your work into categories. The theme comes with three default collections: news, projects, and books. Items from the news collection are automatically displayed on the home page, while items from the projects collection are displayed on a responsive grid on the projects page, and items from the books collection are displayed on its own bookshelf page inside submenus.
You can easily create your own collections for any type of content—teaching materials, courses, apps, short stories, or whatever suits your needs.
Creating a new collection
To create a new collection, follow these steps. We will create a courses collection, but you can replace courses with any name you prefer:
-
Add the collection to
_config.ymlOpen the
collectionssection in _config.yml and add your new collection:collections: news: defaults: layout: post output: true projects: output: true courses: output: true permalink: /courses/:path/output: truemakes the collection items accessible as separate pagespermalinkdefines the URL path for each collection item (:pathis replaced with the filename)- Note: You can customize the permalink structure as needed. If not set, it uses
/COLLECTION_NAME/:name/.
- Note: You can customize the permalink structure as needed. If not set, it uses
-
Create a folder for your collection items
Create a new folder in the root directory with an underscore prefix, matching your collection name. For a
coursescollection, create_courses/:_courses/ ├── course_1.md ├── course_2.md └── course_3.md -
Create a landing page for your collection
Add a Markdown file in
_pages/(e.g.,courses.md) that will serve as the main page for your collection. You can use _pages/projects.md or _pages/books.md as a template and adapt it for your needs.In your landing page, access your collection using the
site.COLLECTION_NAMEvariable:{% assign course_items = site.courses | sort: 'date' | reverse %} {% for item in course_items %} <h3>{{ item.title }}</h3> <p>{{ item.content }}</p> {% endfor %} -
Add a navigation link to your collection page
Update _pages/dropdown.md or the navigation configuration of your page. In the frontmatter of your collection landing page (e.g.,
_pages/courses.md), add:nav: true nav_order: 5nav: truemakes the page appear in the navigation menunav_ordersets the position in the menu (1 = first, 2 = second, etc.)
-
Create collection items
Add Markdown files in your new collection folder (e.g.,
_courses/) with appropriate frontmatter and content.
For more information regarding collections, check Jekyll official documentation and step-by-step guide.
Using frontmatter fields in your collection
When creating items in your collection, you can define custom frontmatter fields and use them in your landing page. For example:
---
layout: page
title: Introduction to Research Methods
importance: 1
category: methods
---
Course description and content here...
Then in your landing page template:
{% if item.category == 'methods' %}
<span class="badge">{{ item.category }}</span>
{% endif %}
Creating a teachings collection
The al-folio theme includes a pre-configured _teachings/ collection for course pages. Each course is represented by a markdown file with frontmatter metadata. Here's how to add or modify courses:
Course file format
Create markdown files in _teachings/ with the following structure:
---
layout: course
title: Course Title
description: Course description
instructor: Your Name
year: 2023
term: Fall
location: Room 101
time: MWF 10:00-11:00
course_id: course-id # This should be unique
schedule:
- week: 1
date: Jan 10
topic: Introduction
description: Overview of course content and objectives
materials:
- name: Slides
url: /assets/pdf/example_pdf.pdf
- name: Reading
url: https://example.com/reading
- week: 2
date: Jan 17
topic: Topic 2
description: Description of this week's content
---
Additional course content, information, or resources can be added here as markdown.
Important course collection notes
- Each course file must have a unique
course_idin the frontmatter - Course files will be grouped by
yearon the teaching page - Within each year, courses are sorted by
term - The content below the frontmatter (written in markdown) will appear on the individual course page
- The schedule section will be automatically formatted into a table
Required fields
layout: course— Must be set to use the course layouttitle— The course titleyear— The year the course was/will be taught (used for sorting)course_id— A unique identifier for the course
Optional fields
description— A brief description of the courseinstructor— The course instructor's nameterm— The academic term (e.g., Fall, Spring, Summer)location— The course locationtime— The course meeting timeschedule— A list of course sessions with details
Collections with categories and tags
If you want to add category and tag support (like the blog posts have), you need to configure the jekyll-archives section in _config.yml. See how this is done with the books collection for reference. For more details, check the jekyll-archives-v2 documentation.
Creating custom metadata groups and archive pages
Beyond the built-in categories and tags fields, you can create custom metadata fields for your collections to organize content in new ways. For example, if you have a book review collection, you might want to organize books by their adaptations (movies, TV shows, video games, etc.).
Understanding Jekyll's special handling of fields
Jekyll has special built-in support for only two fields:
categories– Automatically splits space-separated values into arraystags– Automatically splits space-separated values into arrays
Custom fields (any field name you create) remain as strings and require explicit handling in your Liquid templates.
Example: Adding a custom "adaptations" field
-
Add the field to your collection frontmatter
In your collection item (e.g.,
_books/the_godfather.md):--- layout: book-review title: The Godfather author: Mario Puzo categories: classics crime historical-fiction adaptations: movie TV-series video-game novel-adaptation --- -
Handle the custom field in your layout template
In your layout file (e.g.,
_layouts/book-review.liquid), custom fields must be split into arrays before you can loop over them:{% if page.adaptations %} {% assign page_adaptations = page.adaptations | split: ' ' %} {% for adaptation in page_adaptations %} <a href="{{ adaptation | slugify | prepend: '/books/adaptation/' | relative_url }}"> <i class="fa-solid fa-film fa-sm"></i> {{ adaptation }} </a> {% endfor %} {% endif %}Why the
split: ' 'filter? Becauseadaptationsis a custom field, Jekyll doesn't automatically convert it to an array like it does forcategoriesandtags. Thesplit: ' 'filter breaks the space-separated string into individual items. -
Enable archive pages for your custom field
Add your custom field to the
jekyll-archivesconfiguration in _config.yml:jekyll-archives: posts: enabled: - year - tags - categories books: enabled: - year - tags - categories - adaptations # Add your custom field here permalinks: year: "/:collection/:year/" tags: "/:collection/:type/:name/" categories: "/:collection/:type/:name/" adaptations: "/:collection/:type/:name/" # Add permalink pattern here -
Test your archive pages
After configuration, rebuild your site:
docker compose down docker compose upYour archive pages will be generated at:
/books/adaptations/movie//books/adaptations/tv-series/(slugified fromTV-series)/books/adaptations/video-game/(slugified fromvideo-game)
Each page will automatically display all items with that adaptation value.
Field naming best practices
- Use lowercase words separated by hyphens for multi-word values:
live-action,video-game,TV-series - Choose meaningful names that describe the grouping:
genres,adaptations,media-types,settings, etc. - Keep field values short and consistent across all items in your collection
- Document custom fields in a README or comments for other contributors to understand
Complete example: Book reviews with custom adaptations field
File: _books/the_godfather.md
---
layout: book-review
title: The Godfather
author: Mario Puzo
categories: classics crime historical-fiction
tags: top-100
adaptations: movie TV-series video-game
---
File: _layouts/book-review.liquid (partial)
{% if page.adaptations %}
<div class="adaptations">
<strong>Adaptations:</strong>
{% assign page_adaptations = page.adaptations | split: ' ' %}
{% for adaptation in page_adaptations %}
<a href="{{ adaptation | slugify | prepend: '/books/adaptation/' | relative_url }}">
{{ adaptation }}
</a>
{% unless forloop.last %},{% endunless %}
{% endfor %}
</div>
{% endif %}
File: _config.yml (jekyll-archives section)
jekyll-archives:
books:
enabled:
- year
- categories
- tags
- adaptations
permalinks:
year: "/:collection/:year/"
categories: "/:collection/:type/:name/"
tags: "/:collection/:type/:name/"
adaptations: "/:collection/:type/:name/"
After rebuilding, users can browse books by adaptation at /books/adaptations/movie/, etc.
Adding a new publication
To add publications create a new entry in the _bibliography/papers.bib file. You can find the BibTeX entry of a publication in Google Scholar by clicking on the quotation marks below the publication title, then clicking on "BibTeX", or also in the conference page itself. By default, the publications will be sorted by year and the most recent will be displayed first. You can change this behavior and more in the Jekyll Scholar section in _config.yml file.
You can add extra information to a publication, like a PDF file in the assets/pdfs/ directory and add the path to the PDF file in the BibTeX entry with the pdf field. Some of the supported fields are: abstract, altmetric, annotation, arxiv, bibtex_show, blog, code, dimensions, doi, eprint, hal, html, isbn, pdf, pmid, poster, slides, supp, video, and website.
Author annotation
In publications, the author entry for yourself is identified by string array scholar:last_name and string array scholar:first_name in _config.yml. For example, if you have the following entry in your _config.yml:
scholar:
last_name: [Einstein]
first_name: [Albert, A.]
If the entry matches one form of the last names and the first names, it will be underlined. Keep meta-information about your co-authors in _data/coauthors.yml and Jekyll will insert links to their webpages automatically. The co-author data format is as follows, with the last names lower cased and without accents as the key:
"adams":
- firstname: ["Edwin", "E.", "E. P.", "Edwin Plimpton"]
url: https://en.wikipedia.org/wiki/Edwin_Plimpton_Adams
"podolsky":
- firstname: ["Boris", "B.", "B. Y.", "Boris Yakovlevich"]
url: https://en.wikipedia.org/wiki/Boris_Podolsky
"rosen":
- firstname: ["Nathan", "N."]
url: https://en.wikipedia.org/wiki/Nathan_Rosen
"bach":
- firstname: ["Johann Sebastian", "J. S."]
url: https://en.wikipedia.org/wiki/Johann_Sebastian_Bach
- firstname: ["Carl Philipp Emanuel", "C. P. E."]
url: https://en.wikipedia.org/wiki/Carl_Philipp_Emanuel_Bach
If the entry matches one of the combinations of the last names and the first names, it will be highlighted and linked to the url provided. Note that the keys MUST BE lower cased and MUST NOT contain accents. This is because the keys are used to match the last names in the BibTeX entries, considering possible variations (see related discussion).
Buttons (through custom bibtex keywords)
There are several custom bibtex keywords that you can use to affect how the entries are displayed on the webpage:
abbr: Adds an abbreviation to the left of the entry. You can add links to these by creating a venue.yaml-file in the _data folder and adding entries that match.abstract: Adds an "Abs" button that expands a hidden text field when clicked to show the abstract textaltmetric: Adds an Altmetric badge (Note: if DOI is provided just usetrue, otherwise only add the altmetric identifier here - the link is generated automatically)annotation: Adds a popover info message to the end of the author list that can potentially be used to clarify superscripts. HTML is allowed.arxiv: Adds a link to the Arxiv website (Note: only add the arxiv identifier here - the link is generated automatically)bibtex_show: Adds a "Bib" button that expands a hidden text field with the full bibliography entryblog: Adds a "Blog" button redirecting to the specified linkcode: Adds a "Code" button redirecting to the specified linkdimensions: Adds a Dimensions badge (Note: if DOI or PMID is provided just usetrue, otherwise only add the Dimensions' identifier here - the link is generated automatically)hal: Adds a link to the HAL website (Note: only add the hal identifier (hal-xxx or tel-xxx) here - the link is generated automatically)html: Inserts an "HTML" button redirecting to the user-specified linkpdf: Adds a "PDF" button redirecting to a specified file (if a full link is not specified, the file will be assumed to be placed in the /assets/pdf/ directory)poster: Adds a "Poster" button redirecting to a specified file (if a full link is not specified, the file will be assumed to be placed in the /assets/pdf/ directory)slides: Adds a "Slides" button redirecting to a specified file (if a full link is not specified, the file will be assumed to be placed in the /assets/pdf/ directory)supp: Adds a "Supp" button to a specified file (if a full link is not specified, the file will be assumed to be placed in the /assets/pdf/ directory)website: Adds a "Website" button redirecting to the specified link
You can implement your own buttons by editing the _layouts/bib.liquid file.
Changing theme color
A variety of beautiful theme colors have been selected for you to choose from. The default is purple, but you can quickly change it by editing the --global-theme-color variable in the _sass/_themes.scss file. Other color variables are listed there as well. The stock theme color options available can be found at _sass/_variables.scss. You can also add your own colors to this file assigning each a name for ease of use across the template.
Customizing layout and UI
You can customize the layout and user interface in _config.yml:
back_to_top: true
footer_fixed: true
max_width: 930px
navbar_fixed: true
back_to_top: Displays a "back to top" button in the footer. When clicked, it smoothly scrolls the page back to the top.footer_fixed: Whentrue, the footer remains fixed at the bottom of the viewport. Whenfalse, it appears at the end of the page content.max_width: Controls the maximum width of the main content area in pixels. The default is930px. You can adjust this to make your content wider or narrower.navbar_fixed: Whentrue, the navigation bar stays fixed at the top of the page when scrolling. Whenfalse, it scrolls with the page content.
Adding social media information
Social media information is managed through the jekyll-socials plugin. To add your social media links:
- Edit
_data/socials.ymlto add your social profiles - The plugin will automatically display the social icons based on the order they are defined in the file (see the comments at the top of
_data/socials.yml)
The template supports icons from:
Social media links will appear at the bottom of the About page and in the search results by default. You can customize this behavior in _config.yml:
enable_navbar_social: true– Display social links in the navigation barsocials_in_search: false– Remove social links from search results
For more details, see the jekyll-socials documentation.
Adding a newsletter
You can add a newsletter subscription form by adding the specified information at the newsletter section in the _config.yml file. To set up a newsletter, you can use a service like Loops.so, which is the current supported solution. Once you have set up your newsletter, you can add the form endpoint to the endpoint field in the newsletter section of the _config.yml file.
Depending on your specified footer behavior, the sign up form either will appear at the bottom of the About page and at the bottom of blogposts if related_posts are enabled, or in the footer at the bottom of each page.
Configuring search features
The theme includes a powerful search functionality that can be customized in _config.yml:
bib_search: true
posts_in_search: true
search_enabled: true
socials_in_search: true
bib_search: Enables search within your publications/bibliography. When enabled, a search box appears on the publications page, allowing visitors to filter publications by title, author, venue, or year.posts_in_search: Includes blog posts in the search index. Users can search for posts by title, content, or tags.search_enabled: Enables the site-wide search feature. When enabled, a search box appears in the navigation bar, allowing users to search across your site content.socials_in_search: Includes your social media links and contact information in search results. This makes it easier for visitors to find ways to connect with you.
All these search features work in real-time and do not require a page reload.
Social media previews
al-folio supports Open Graph (OG) meta tags, which create rich preview objects when your pages are shared on social media platforms like Twitter, Facebook, LinkedIn, and others. These previews include your site's image, title, and description.
How to enable
To enable social media previews:
-
Open
_config.ymland set:serve_og_meta: true -
Rebuild your site:
docker compose down && docker compose up # or bundle exec jekyll serve
Once enabled, all your site's pages will automatically include Open Graph meta tags in the HTML head element.
Configuring preview images
You can configure what image displays in social media previews on a per-page or site-wide basis.
Site-wide default image:
Add the following to _config.yml:
og_image: /assets/img/your-default-preview-image.png
Replace the path with your actual image location in assets/img/.
Per-page custom image:
To override the site-wide default for a specific page, add og_image to the page's frontmatter:
---
layout: page
title: My Page
og_image: /assets/img/custom-preview-image.png
---
Preview image best practices
- Dimensions: Use 1200×630 pixels for optimal display on most social media platforms
- Format: PNG or JPG formats work best
- Size: Keep file size under 5MB
- Content: Ensure the image clearly represents your page content
When a page is shared on social media, the platform will display your configured image along with the page title, description (from your site title or page description), and URL.
Related posts
The theme can automatically display related posts at the bottom of each blog post. These are selected by finding the most recent posts that share common tags with the current post.
How it works
- By default, the most recent posts that share at least one tag with the current post are displayed
- You can customize how many posts are shown and how many tags must match
- You can disable related posts for individual posts or across your entire site
Configuration
To customize related posts behavior, edit the related_blog_posts section in _config.yml:
related_blog_posts:
enabled: true
max_related: 5
enabled: Set totrue(default) to show related posts, orfalseto disable them site-widemax_related: Maximum number of related posts to display (default: 5)
The theme also uses tags to find related content. Make sure your blog posts include relevant tags in their frontmatter:
---
layout: post
title: My Blog Post
tags: machine-learning python
---
Disable related posts for a specific post
To hide related posts on an individual blog post, add this to the post's frontmatter:
---
layout: post
title: My Blog Post
related_posts: false
---
Additional configuration in _config.yml
You can also customize related posts behavior with these settings:
related_blog_posts:
enabled: true
max_related: 5
These settings control:
- Which posts are considered "related" (based on shared tags)
- How many related posts to display
- The algorithm used to calculate post similarity (uses the
classifier-reborngem)
Managing publication display
The theme offers several options for customizing how publications are displayed:
enable_publication_thumbnails: true
max_author_limit: 3
more_authors_animation_delay: 10
enable_publication_thumbnails: Whentrue, displays preview images for publications (if specified in the BibTeX entry with thepreviewfield). Set tofalseto disable thumbnails for all publications.max_author_limit: Sets the maximum number of authors shown initially for each publication. If a publication has more authors, they are hidden behind a "more authors" link. Leave blank to always show all authors.more_authors_animation_delay: Controls the animation speed (in milliseconds) when revealing additional authors. A smaller value means faster animation.
To add a thumbnail to a publication, include a preview field in your BibTeX entry:
@article{example2024,
title={Example Paper},
author={Author, First and Author, Second},
journal={Example Journal},
year={2024},
preview={example_preview.png}
}
Place the image file in assets/img/publication_preview/.
Adding a Google Calendar
You can embed a Google Calendar on any page by using the calendar.liquid include.
Basic usage
Add the following to your page's Markdown file (for example, in _pages/teaching.md):
{% include calendar.liquid calendar_id='your-calendar-id@group.calendar.google.com' timezone='Your/Timezone' %}
Replace:
your-calendar-id@group.calendar.google.comwith your actual Google Calendar ID (found in Google Calendar Settings → Integrate calendar → Calendar ID)Your/Timezonewith your timezone (e.g.,UTC,Asia/Shanghai,America/New_York). The default isUTC.
Enable the calendar script for your page
To enable the calendar on your page, add calendar: true to the frontmatter:
---
layout: page
title: teaching
calendar: true
---
This setting prevents unnecessary script loading for pages that don't display a calendar.
Optional: Customize the calendar style
You can optionally customize the iframe styling using the style parameter:
{% include calendar.liquid calendar_id='your-calendar-id@group.calendar.google.com' timezone='UTC' style='border:0; width:100%; height:800px;' %}
The default style is border:0; width:100%; height:600px;.
Updating third-party libraries
The theme uses various third-party JavaScript and CSS libraries. You can manage these in the third_party_libraries section of _config.yml:
third_party_libraries:
download: false
bootstrap-table:
version: "1.22.4"
url:
css: "https://cdn.jsdelivr.net/npm/bootstrap-table@{{version}}/dist/bootstrap-table.min.css"
js: "https://cdn.jsdelivr.net/npm/bootstrap-table@{{version}}/dist/bootstrap-table.min.js"
integrity:
css: "sha256-..."
js: "sha256-..."
download: Whenfalse(default), libraries are loaded from CDNs. Whentrue, the specified library versions are downloaded during build and served from your site. This can improve performance but increases your repository size.version: Specifies which version of each library to use. Update this to use a newer version.url: Template URLs for loading the library. The{{version}}placeholder is replaced with the version number automatically.integrity: Subresource Integrity (SRI) hashes ensure that the library hasn't been tampered with. When updating a library version, you should also update its integrity hash.
To update a library:
- Change the
versionnumber - Obtain the new integrity hash for the updated library version and update the
integrityfield with the new hash. You can:-
Check if the CDN provider (e.g., jsDelivr, cdnjs, unpkg) provides the SRI hash for the file. Many CDN sites display the SRI hash alongside the file URL.
-
Generate the SRI hash yourself using a tool such as SRI Hash Generator or by running the following command in your terminal:
curl -sL [FILE_URL] | openssl dgst -sha384 -binary | openssl base64 -AReplace
[FILE_URL]with the URL of the library file. Then, prefix the result withsha384-and use it in theintegrityfield. For detailed instructions on updating specific libraries, see the FAQ:
-
Removing content
Since this template has a lot of content, you may want to remove some of it. The easiest way to achieve this and avoid merge conflicts when updating your code (as pointed by CheariX ) is to add the unwanted files to the exclude section in your _config.yml file instead of actually deleting them, for example:
exclude:
- _news/announcement_*.md
- _pages/blog.md
- _posts/
- _projects/?_project.md
- assets/jupyter/blog.ipynb
Here is a list of the main components that you may want to delete, and how to do it. Don't forget if you delete a page to update the nav_order of the remaining pages.
Removing the blog page
To remove the blog, you have to:
- delete _posts directory
- delete blog page _pages/blog.md
- remove reference to blog page in our _pages/dropdown.md
- remove the
latest_postspart in _pages/about.md - remove the
Blogsection in the _config.yml file and the related parts, like thejekyll-archives
You can also:
- delete _includes/latest_posts.liquid
- delete _includes/related_posts.liquid
- delete _layouts/archive.liquid (unless you have a custom collection that uses it)
- delete _plugins/external-posts.rb
- remove the
jekyll-archives-v2gem from the Gemfile and thepluginssection in _config.yml (unless you have a custom collection that uses it) - remove the
classifier-reborngem from the Gemfile
Removing the news section
To remove the news section, you can:
- delete the _news directory
- delete the file _includes/news.liquid and the references to it in the _pages/about.md
- remove the
announcementspart in _pages/about.md - remove the news part in the
Collectionssection in the _config.yml file
Removing the projects page
To remove the projects, you can:
- delete the _projects directory
- delete the projects page _pages/projects.md
- remove reference to projects page in our _pages/dropdown.md
- remove projects part in the
Collectionssection in the _config.yml file
You can also:
Removing the publications page
To remove the publications, you can:
- delete the _bibliography directory
- delete the publications page _pages/publications.md
- remove reference to publications page in our _pages/dropdown.md
- remove
Jekyll Scholarsection in the _config.yml file
You can also:
- delete the _layouts/bib.liquid file
- delete _includes/bib_search.liquid
- delete _includes/citation.liquid
- delete _includes/selected_papers.liquid
- delete _plugins/google-scholar-citations.rb
- delete _plugins/hide-custom-bibtex.rb
- delete _plugins/inspirehep-citations.rb
- remove the
jekyll-scholargem from the Gemfile and thepluginssection in _config.yml
Removing the repositories page
To remove the repositories, you can:
- delete the repositories page _pages/repositories.md
- delete _includes/repository/ directory
You can also remove pages through commenting out front-matter blocks
For .md files in _pages directory, if you do not want to completely edit or delete them but save for later use, you can temporarily disable these variables. But be aware that Jekyll only recognizes front matter when it appears as uncommented. The layout, permalink, and other front-matter behavior are disabled for that file.
For example, books.md do:
<!-- ---
layout: book-shelf
title: bookshelf
permalink: /books/
nav: true
collection: books
--- -->
> What an astonishing thing a book is. It's a flat object made from a tree with flexible parts on which are imprinted lots of funny dark squiggles. But one glance at it and you're inside the mind of another person, maybe somebody dead for thousands of years. Across the millennia, an author is speaking clearly and silently inside your head, directly to you. Writing is perhaps the greatest of human inventions, binding together people who never knew each other, citizens of distant epochs. Books break the shackles of time. A book is proof that humans are capable of working magic.
>
> -- Carl Sagan, Cosmos, Part 11: The Persistence of Memory (1980)
## Books that I am reading, have read, or will read
Adding Token for Lighthouse Badger
To add secrets for lighthouse-badger, create a personal access token (PAT) and add it as a secret named LIGHTHOUSE_BADGER_TOKEN to your repository. The lighthouse-badger documentation specifies using an environment variable, but using it as a secret is more secure and appropriate for a PAT.
Also In case you face the error: "Input required and not supplied: token" in the Lighthouse Badger action, this solution resolves it.
Personal Access Token (fine-grained) Permissions for Lighthouse Badger:
- contents: access: read and write
- metadata: access: read-only
Due to the necessary permissions (PAT and others mentioned above), it is recommended to use it as a secret rather than an environment variable.
Customizing fonts, spacing, and more
The _sass/ directory contains specialized SCSS files organized by feature and usage. To customize fonts, spacing, colors, and other styles, edit the relevant file based on what you're modifying:
- Typography: Edit
_typography.scssto change fonts, heading styles, links, tables, and blockquotes. - Navigation: Edit
_navbar.scssto customize the navigation bar and dropdown menus. - Colors and themes: Edit
_themes.scssto change theme colors and_variables.scssfor global variables. - Blog styles: Edit
_blog.scssto customize blog post listings, tags, and pagination. - Publications: Edit
_publications.scssto modify bibliography and publication display styles. - Components: Edit
_components.scssto customize reusable components like cards, profiles, and projects. - Code and utilities: Edit
_utilities.scssfor code highlighting, forms, modals, and animations. - Layout: Edit
_layout.scssfor overall page layout styles.
The easiest way to preview changes in advance is by using Chrome dev tools or Firefox dev tools. Inspect elements to see which styles apply and experiment with changes before editing the SCSS files. For more information on how to use these tools, check Chrome and Firefox how-tos, and this tutorial.
Scheduled Posts
al-folio contains a workflow which automatically publishes all posts scheduled at a specific day, at the end of the day (23:30). By default the action is disabled, and to enable it you need to go to .github/workflows/ and find the file called schedule-posts.txt. This is the workflow file. For GitHub to recognize it as one (or to enable the action), you need to rename it to schedule-posts.yml.
In order to use this you need to save all of your "Completed" blog posts which are scheduled to be uploaded on a specific date, in a folder named _scheduled/ in the root directory.
Incomplete posts should be saved in
_drafts/
Name Format
In this folder you need to store your file in the same format as you would in _posts/
Example file name:
2024-08-26-This file will be uploaded on 26 August.md
Important Notes
- The scheduler uploads posts everyday at 🕛 23:30 UTC
- It will only upload posts at 23:30 UTC of their respective scheduled days, It's not uploaded in 23:59 in case there are a lot of files as the scheduler must finish before 00:00
- It will only upload files which follow the pattern
yyyy-mm-dd-title.md- This means that only markdown files will be posted
- It means that any markdown which do not follow this pattern will not be posted
- The scheduler works by moving posts from the
_scheduled/directory to_posts/, it will not post to folders like_projects/or_news/ - The date in the name of the file is the day that file will be uploaded on
2024-08-27-file1.mdwill not be posted before or after 27-August-2024 (Scheduler only works for posts scheduled on the present day)2025-08-27-file2.mdwill be posted exactly on 27-August-2025File3.mdwill not be posted at all2026-02-31-file4.mdis supposed to be posted on 31-February-2026, but there is no 31st in February hence this file will never be posted either
GDPR Cookie Consent Dialog
al-folio includes a built-in GDPR-compliant cookie consent dialog to help you respect visitor privacy and comply with privacy regulations (GDPR, CCPA, etc.). The feature is powered by Vanilla Cookie Consent and integrates with all analytics providers.
How it works
- A consent dialog appears on the visitor's first visit to your site
- Visitors can accept all, reject all, or customize preferences for analytics cookies
- Analytics scripts (Google Analytics, Cronitor, Pirsch, Openpanel) are blocked by default and only run after explicit consent
- Google Consent Mode ensures Google services operate in privacy mode before consent is granted
- User preferences are saved in their browser and respected on subsequent visits
- The dialog is mobile-responsive and supports multiple languages
When to use
- ✅ Required if your site serves EU visitors and uses any analytics
- ✅ Recommended for any website using analytics, tracking, or marketing tools
- ❌ Not needed if your site doesn't use any analytics providers
How to enable
-
Open
_config.ymland locate the following line:enable_cookie_consent: false -
Change it to:
enable_cookie_consent: true -
Rebuild your site:
docker compose down && docker compose up # or bundle exec jekyll serve -
The consent dialog will automatically appear on your site's homepage on first visit
Customizing the consent dialog
The consent dialog configuration and messages are defined in _scripts/cookie-consent-setup.js. You can customize:
- Dialog titles and button labels
- Cookie categories and descriptions
- Contact information links (points to
#contactby default) - Language translations
To modify the dialog, edit the language.translations.en section in _scripts/cookie-consent-setup.js. For example, to change the consent dialog title:
consentModal: {
title: 'Your custom title here',
description: 'Your custom description...',
// ... other options
}
Supported analytics providers
When cookie consent is enabled, these analytics providers are automatically blocked until the user consents:
- Google Analytics (GA4) – Uses Google Consent Mode for privacy-first operation before consent
- Cronitor RUM – Real User Monitoring for performance tracking
- Pirsch Analytics – GDPR-compliant analytics alternative
- Openpanel Analytics – Privacy-focused analytics platform
Each provider only collects data if:
- It's enabled in
_config.yml(e.g.,enable_google_analytics: true) - The user has granted consent to the "analytics" category in the consent dialog
How it integrates with analytics
When enable_cookie_consent: true, the template automatically:
- Adds
type="text/plain" data-category="analytics"to all analytics script tags - This tells the cookie consent library to block these scripts until consent is granted
- Loads the consent library and initializes Google Consent Mode
- Updates consent preferences when the user changes them in the dialog
You don't need to modify any analytics configuration—it works automatically.
For developers
If you want to programmatically check consent status or react to consent changes, the library exposes the following:
// Check if user has granted analytics consent
window.CookieConsent.getCategories().analytics; // returns true or false
// Listen for consent changes
window.CookieConsent.onChange(function (consentData) {
// Handle consent change
});
For more API details, see Vanilla Cookie Consent documentation.
Setting up a Personal Access Token (PAT) for Google Scholar Citation Updates
Tip
After setting up al-folio you may want to run
python3 bin/update_citations.pyto fill the_data/citations.ymlfile with your Google Scholar citation counts.
This project includes an automated workflow to update the citation counts for your publications using Google Scholar.
The workflow commits changes to _data/citations.yml directly to the main branch.
By default, the GITHUB_TOKEN will be used to commit the changes.
However, this token does not have permission to trigger subsequent workflows, such as the site rebuild workflow.
In order to deploy the changes from main, you can manually trigger the deploy workflow.
Tip
To ensure that these commits can trigger further GitHub Actions workflows (such as site rebuilds), you can use a Personal Access Token (PAT) instead of the default GitHub Actions token. If you have set up a PAT, citation updates will trigger further workflows (such as site rebuilds) after committing changes. In order to run the action with a PAT, you need to uncomment the following lines from the workflow file (
update-citations.yml):with: token: ${{ secrets.PAT }}
Why is a PAT required?
GitHub restricts the default GITHUB_TOKEN from triggering other workflows when a commit is made from within a workflow. Using a PAT overcomes this limitation and allows for full automation.
How to set up the PAT
-
Create a Personal Access Token
- Go to GitHub Settings > Developer settings > Personal access tokens.
- Click "Generate new token" (classic or fine-grained).
- Grant at least the following permissions:
repo(for classic tokens if repo is private),public_repo(for classic tokens if repo is public) orcontents: read/write(for fine-grained tokens)
- Save the token somewhere safe.
-
Add the PAT as a repository secret
- Go to your repository on GitHub.
- Navigate to
Settings>Secrets and variables>Actions>New repository secret. - Name the secret
PAT(must match the name used in the workflow). - Paste your PAT and save.
-
Workflow usage The workflow
.github/workflows/update-citations.ymluses this PAT to commit updates to_data/citations.yml.