Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

Community websites with Eleventy

Update: I did a talk about this in THE Eleventy Meetup 20.2.2024, you can watch a recording in Youtube.

Eleventy is a great and powerful static site generator especially for event-organizing communities like meetups. Here’s my setup that I use with both turkufrontend.fi and archipylago.dev.

On the root, I have _data folder with 5 JSON files: events.json, history.json , team.json, sponsors.jsonand speakers.json.

Events

For event logs, I have two files. One keeps track of current calendar (usually one spring or fall at a time) and another one is a full history of previous events.

Calendar

events.json has an array of objects with three keys. Dates are in YYYY-MM-DD format.

[
 {
    "date": "YYYY-MM-DD",
    "host": "[Sponsor name]",
    "url": "[URL for registration / event info]"
 }
]

With events.njk partial, these get rendered (with Nunjucks) into a list as

<section>
  <h2>Fall 2023</h2>
  <div class="event-container">
   <ul id="events">
      {% for event in events %}
      <li>
        {{ event.date | toLocalDate }} @ 
				{% if event.url %}
	        <a href="{{ event.url }}">
	          {{ event.host }}
	        </a>
        {% else %}
	        {{ event.host | formatHost }}
        {% endif %}
      </li>
      {% endfor %}
    </ul>
  </div>
  <div style="text-align: center">
    <a href="{{ '/history' | url }}">full history</a>
  </div>
</section>

Each event lists the date and host and if URL is present, it links to the event page.

Full timeline

For a full history of events, history.json has an array of objects such as

{
    "date": "YYYY-MM-DD",
    "host": "[Sponsor name]",
    "talks": [
      {
        "title": "[Talk title]",
        "speaker": "[Speaker name]",
        "description": "[Talk abstract]",
        "url": "[URL to slides/recording/etc]"
      }
    ]
  }

These are rendered with history.njk partial as

<div class="timeline-event">
    <div class="timeline-header">
      <div class="timeline-date">
        {{ event.date | toLocalDateYear }}
      </div>
      <div class="timeline-host">
        {{ event.host | formatSponsorLogoNoLink | safe }}
      </div>
    </div>
    <div class="timeline-talks">
      <ul>
        {% for talk in event.talks %}
        <li>
          <strong>{{ talk.title }}</strong> by {{ talk.speaker }}
        </li>
        {% endfor %}
      </ul>
    </div>
  </div>

Speakers, Sponsors and Team

Speakers

For speakers, I store a list of names in an array

[
  "First Last",
  "Firstname Lastname"
]

and accompanied with that, in assets/img/speakers/ I have portraits of the speakers in file name format firstname-lastname.png.

To render them into our hall of fame, I have a partial of

<div class="gallery">
  {% for name in speakers %}
	  {{ name | formatHallOfFame | safe }}
  {% endfor %}
</div>

where the formatHallOfFame filter looks like

formatHallOfFame: function (name) {
    return `<div class="speaker">
    <img src="/assets/img/speakers/${slugify(name)}.png" alt="">
    <p>${name}</p>
    </div>`;
  }

Sponsors

Sponsors work similarly but has extra attribute of URL:

{
  "name": "[Sponsor name]",
  "url": "[URL to sponsor website]"
}

and these are rendered with

<section>
  <h2>We are working with</h2>
  <div id="sponsors">
    {% for sponsor in sponsors %}
	    {{ sponsor | formatSponsorLogo | safe }}
    {% endfor %}
  </div>
</section>

with the filter

formatSponsorLogo: function ({ name, url }) {
  const filename = `/assets/img/sponsors/${slugify(name)}.png`;

  return `<a href="${url}" target=_blank><img src="${filename}" alt="${name}"></a>`;
},

Team

And finally the team is stored in team.json with each organizing team member in an array:

[
	{
	  "name": "[name]",
	  "title": "[title]",
	  "links": [
	    {
	      "icon": "[icon-name]",
	      "url": "[url]"
	    }
	  ]
	}
]

where icon-name maps to SVGs I have stored. For example, "icon": "mastodon" renders mastodon.svg icon.

This is rendered with

<section>
  <h2>Team</h2>
  <div id="team">
    {% for person in team %}
    <div class="profile">
      {{ person.name | nameToImage | safe }}
      <div class="profile--inner">
        <p class="profile--name">
          {{ person.name }}
        </p>
        <p class="profile--title">
          {{ person.title }}
        </p>
        <div class="profile--links">
          {% for link in person.links %}
	          {{ link | linkToHTML | safe }}
          {% endfor %}
        </div>
      </div>
    </div>
    {% endfor %}
  </div>
</section>

Updating the website

Since all these are stored in data files and rendered from them, making updates becomes a process of adding images (in case of new speakers/sponsors) and updating JSON files.

And since all the partials are in the _includes/ folder, they can be included in any part of the website, making them flexible components which makes making changes to the layouts easier when everything is generated.

Comments

Comment by replying to this post in Mastodon.

Loading comments...

Continue discussion in Mastodon »

Syntax Error

Sign up for Syntax Error, a monthly newsletter that helps developers turn a stressful debugging situation into a joyful exploration.