Keep it separated: content and presentation
In web development we aim to keep content separate from presentation. That's why we use semantic HTML markup that can be styled with our CSS instructions. Equally so, in Pelican we have our data/content in our Pelican project and apply our styling through the templates in our theme. This is an important distinction. In Pelican:
- We keep our content in the Pelican project
- We keep our styling in our Pelican theme
- The Pelican theme consists of:
- HTML templates that may contain Jinja directives
- CSS/SASS declarations
- Assets (theme images/JavaScript)
The problem: Pelican's default boundaries
Out of the box Pelican provides few options for handling more complex templates. While Pelican's default data processing is great for simpler templates that have few data types on a page, we quickly feel stifled when we want to come up with more advanced designs.
Let's consider an example. Say we want to create a template that displays customer testimonials. Ideally, we'd use a template that processes "testimonial" data set in Pelican's content pages. We want to avoid hardcoding content into our templates if we can avoid it. After all, that would defeat the purpose of templates.
In order to develop such more complex templates, we need to be able to pass content to them for processing. If we were not able to do this, we might as well not use templates at all.
Note
Simply put:
- Keep content on the Pelican project side (e.g., Markdown files)
- Process that content by templates of the Pelican theme
- Attempt to keep HTML out of content pages as much as possible
- Themes may be tweaked per project as needed (i.e., we will assume that the theme will be tailored specifically for this project)
So, what options are available to us to solve this dilemma?
The challenge: displaying structured data
In our example, we want to display testimonials in a structured format. Let's say our HTML looks like this:
<section class="section testimonial">
<div class="container">
<div class="row testimonial-wrap">
<div class="testimonial-item position-relative">
<i class="ti-quote-left text-color"></i>
<div class="testimonial-item-content">
<p class="testimonial-text">Will buy again.</p>
<div class="testimonial-author">
<h5 class="mb-0 text-capitalize">John</h5>
<p>Client</p>
</div>
</div>
</div>
<div class="testimonial-item position-relative">
<i class="ti-quote-left text-color"></i>
<div class="testimonial-item-content">
<p class="testimonial-text">I'm so happy with your service!</p>
<div class="testimonial-author">
<h5 class="mb-0 text-capitalize">Jill</h5>
<p>Business owner</p>
</div>
</div>
</div>
</div>
</div>
</section>
If we were to use Pelican out of the box, how would we be able to get this content to appear on our site? We could drop this entire HTML block into our content pages or our theme. Neither option is all that appealing, so we'll investigate other avenues.
The solution: YAML frontmatter for complex data structures
My Pelican environment is set up for YAML frontmatter on Markdown pages by default. Frontmatter processing is handled by the FrontMark plugin. This works very well for our use case.
Step 1: add structured data to frontmatter
We'll add some frontmatter to our content page:
---
title: "Testimonial"
data:
testimonials:
- name: John
position: Client
blurb: Will buy again.
- name: Jill
position: Business owner
blurb: Great service!
---
Step 2: create a template to process the data
Having added our frontmatter to our content, we now need it to be forwarded to our template so that it can be processed. FrontMark makes the data available as data objects that we can reference in our templates. Let's create a testimonial template called templates/monial.html and add the following code:
{% extends "page.html" %}
{% block monial %}
<section class="section testimonial">
<div class="container">
<div class="row testimonial-wrap">
{% for testimonial in page.data.testimonials %}
<div class="testimonial-item position-relative">
<i class="ti-quote-left text-color"></i>
<div class="testimonial-item-content">
<p class="testimonial-text">{{ testimonial.blurb }}</p>
<div class="testimonial-author">
<h5 class="mb-0 text-capitalize">{{ testimonial.name }}</h5>
<p>{{ testimonial.position }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endblock %}
In the code above, we refer to our data with page.data.testimonials. We can now loop through this object to get our individual results (i.e., the testimonial entries).
Step 3: update your base templates
Next, we'll reference this template in our templates/page.html template:
{% block content %}
<div class="container">
<article class="article">
<div class="content">
{{ page.content }}
{% block monial %}{% endblock %}
</div>
</article>
</div>
{% endblock content %}
In templates/base.html we have:
<div class="main-wrapper {% block title_bg_image %}{% endblock %}"
id="main-wrapper">
{% block page_title %}{% endblock %}
{% block content %}{% endblock %}
</div>
Step 4: specify the template in your content page
Last, we'll make sure the Pelican content page uses the templates/monial.html template by adding template: monial to it:
---
title: "Testimonial"
data:
testimonials:
- name: John
position: Client
blurb: Will buy again.
- name: Jill
position: Business owner
blurb: I'm so happy with your service!
template: monial
---
Results and benefits
Now, when we regenerate our site, we should see the testimonials printed as we wanted, proving that it is possible to largely keep our data on the content side of our Pelican project while processing it through theme templates.
This approach provides several advantages:
- Maintains a clean separation between content and presentation
- Enables complex data structures within Markdown files
- Allows for reusable, data-driven templates
- Keeps HTML markup in the theme where it belongs
- Makes content maintenance simpler and more focused
Next steps
With this technique, you can create more advanced templates for various data structures:
- Team member profiles
- Product features and specifications
- Event details and schedules
- Portfolio or project showcases
By leveraging YAML frontmatter and custom templates, you can build sophisticated Pelican websites without compromising on the separation of concerns principle.