Tag: CommunityDriven

A Community-Driven Site with Eleventy: Building the Site

In the last article, we learned what goes into planning for a community-driven site. We saw just how many considerations are needed to start accepting user submissions, using what I learned from my experience building Style Stage as an example.

Now that we’ve covered planning, let’s get to some code! Together, we’re going to develop an Eleventy setup that you can use as a starting point for your own community (or personal) site.

Article Series:

  1. Preparing for Contributions
  2. Building the Site (You are here!)

This article will cover:

  • How to initialize Eleventy and create useful develop and build scripts
  • Recommended setup customizations
  • How to define custom data and combine multiple data sources
  • Creating layouts with Nunjucks and Eleventy layout chaining
  • Deploying to Netlify

The vision

Let’s assume we want to let folks submit their dogs and cats and pit them against one another in cuteness contests.

Screenshot of the site, showing a Meow vs. Bow Wow heading above a Weekly Battle subheading, followed by a photo of a tabby cat named Fluffy and one of a happy dog named Lexi.
Live demo

We’re not going to get into user voting in this article. That would be so cool (and totally possible with serverless functions) but our focus is on the pet submissions themselves. In other words, users can submit profile details for their cats and dogs. We’ll use those submissions to create a weekly battle that puts a random cat up against a random dog on the home page to duke it out over which is the most purrrfect (or woof-tastic, if you prefer).

Let’s spin up Eleventy

We’ll start by initializing a new project by running npm init on any directory you’d like, then installing Eleventy into it with:

npm install @11ty/eleventy

While it’s totally optional, I like to open up the package-json file that’s added to the directory and replace the scripts section with this:

"scripts": {   "develop": "eleventy --serve",   "build": "eleventy" },

This allows us to start developing Eleventy in a development environment (npm run develop) that includes Browsersync hot-reloading for local development. It also adds a command that compiles and builds our work (npm run build) for deployment on a production server.

If you’re thinking, “npm what?” what we’re doing is calling on Node (which is something Eleventy requires). The commands noted here are intended to be run in your preferred terminal, which may be an additional program or built-in to your code editor, like it is in VS Code.

We’ll need one more npm package, fast-glob, that will come in handy a little later for combining data. We may as well install it now:

npm install --save-dev fast-glob.

Let’s configure our directory

Eleventy allows customizing the input directory (where we work) and output directory (where our built work goes) to provide a little extra organization.

To configure this, we’ll create the eleventy.js file at the root of the project directory. Then we’ll tell Eleventy where we want our input and output directories to go. In this case, we’re going to use a src directory for the input and a public directory for the output.

module.exports = function (eleventyConfig) {   return      dir: {       input: "src",       output: "public"     },   }; };

Next, we’ll create a directory called pets where we’ll store the pets data we get from user submissions. We can even break that directory down a little further to reduce merge conflicts and clearly distinguish cat data from dog data with cat and dog subdirectories:

pets/   cats/   dogs/

What’s the data going to look like? Users will send in a JSON file that follows this schema, where each property is a data point about the pet:

{   "name": "",   "petColor": "",   "favoriteFood": "",   "favoriteToy": "",   "photoURL": "",   "ownerName": "",   "ownerTwitter": "" }

To make the submission process crystal clear for users, we can create a CONTRIBUTING.md file at the root of the project and write out the guidelines for submissions. GitHub takes the content in this file and uses displays it in the repo. This way, we can provide guidance on this schema such as a note that favoriteFood, favoriteToy, and ownerTwitte are optional fields.

A README.md file would be just as fine if you’d prefer to go that route. It’s just nice that there’s a standard file that’s meant specifically for contributions.

Notice photoURL is one of those properties. We could’ve made this a file but, for the sake of security and hosting costs, we’re going to ask for a URL instead. You may decide that you are willing to take on actual files, and that’s totally cool.

Let’s work with data

Next, we need to create a combined array of data out of the individual cat files and dog files. This will allow us to loop over them to create site pages and pick random cat and dog submissions for the weekly battles.

Eleventy allows node module.exports within the _data directory. That means we can create a function that finds all cat files and another that finds all dog files and then creates arrays out of each set. It’s like taking each cat file and merging them together to create one data set in a single JavaScript file, then doing the same with dogs.

The filename used in _data becomes the variable that holds that dataset, so we’ll add files for cats and dogs in there:

_data/   cats.js   dogs.js

The functions in each file will be nearly identical — we’re merely swapping instances of “cat” for “dog” between the two. Here’s the function for cats: 

const fastglob = require("fast-glob"); const fs = require("fs"); 
 module.exports = async () => {   // Create a "glob" of all cat json files   const catFiles = await fastglob("./src/pets/cats/*.json", {     caseSensitiveMatch: false,   }); 
   // Loop through those files and add their content to our `cats` Set   let cats = new Set();   for (let cat of catFiles) {     const catData = JSON.parse(fs.readFileSync(cat));     cats.add(catData);   } 
   // Return the cats Set of objects within an array   return [...cats]; };

Does this look scary? Never fear! I do not routinely write node either, and it’s not a required step for less complex Eleventy sites. If we had instead chosen to have contributors add to an ever growing single JSON file with _data, then this combination step wouldn’t be necessary in the first place. Again, the main reason for this step is to reduce merge conflicts by allowing for individual contributor files. It’s also the reason we added fast-glob to the mix.

Let’s output the data

This is a good time to start plugging data into the templates for our UI. In fact, go ahead and drop a few JSON files into the pets/cats and pets/dogs directories that include data for the properties so we have something to work with right out of the gate and test things.

We can go ahead and add our first Eleventy page by adding a index.njk file in the src directory. This will become the home page, and is a Nunjucks template file format.

Nunjucks is one option of many for creating templates with Eleventy. See the docs for a full list of templating options.

Let’s start by looping over our data and outputting an unordered list both for cats and dogs:

<ul>   <!-- Loop through cat data -->   {% for cat in cats %}   <li>     <a href="/cats/{{ cat.name | slug }}/">{{ cat.name }}</a>   </li>   {% endfor %} </ul> 
 <ul>   <!-- Loop through dog data -->   {% for dog in dogs %}   <li>     <a href="/dogs/{{ dog.name | slug }}/">{{ dog.name }}</a>   </li>   {% endfor %} </ul>

As a reminder, the reference to cats and dogs matches the filename in _data. Within the loop we can access the JSON keys using dot notation, as seen for cat.name, which is output as a Nunjucks template variable using double curly braces (e.g. {{ cat.name }}).

Let’s create pet profile pages

Besides lists of cats and dogs on the home page (index.njk), we also want to create individual profile pages for each pet. The loop indicated a hint at the structure we’ll use for those, which will be [pet type]/[name-slug].

The recommended way to create pages from data is via the Eleventy concept of pagination which allows chunking out data.

We’re going to create the files responsible for the pagination at the root of the src directory, but you could nest them in a custom directory, as long as it lives within src and can still be discovered by Eleventy.

src/   cats.njk   dogs.njk

Then we’ll add our pagination information as front matter, shown for cats:

--- pagination:   data: cats   alias: cat   size: 1 permalink: "/cats/{{ cat.name | slug }}/" ---

The data value is the filename from _data. The alias value is optional, but is used to reference one item from the paginated array. size: 1 indicates that we’re creating one page per item of data.

Finally, in order to successfully create the page output, we need to also indicate the desired permalink structure. That’s where the alias value above comes into play, which accesses the name key from the dataset. Then we are using a built-in filter called slug that transforms a string value into a URL-friendly string (lowercasing and converting spaces to dashes, etc).

Let’s review what we have so far

Now is the time to fire up Eleventy with npm run develop. That will start the local server and show you a URL in the terminal you can use to view the project. It will show build errors in the terminal if there are any.

As long as all was successful, Eleventy will create a public directory, which should contain:

public/   cats/     cat1-name/index.html     cat2-name/index.html   dogs/     dog1-name/index.html     dog2-name/index.html   index.html

And in the browser, the index page should display one linked list of cat names and another one of linked dog names.

Let’s add data to pet profile pages

Each of the generated pages for cats and dogs is currently blank. We have data we can use to fill them in, so let’s put it to work.

Eleventy expects an _includes directory that contains layout files (“templates”) or template partials that are included in layouts.

We’ll create two layouts:

src/   _includes/     base.njk     pets.njk

The contents of base.njk will be an HTML boilerplate. The <body> element in it will include a special template tag, {{ content | safe }}, where content passed into the template will render, with safe meaning it can render any HTML that is passed in versus encoding it.

Then, we can assign the homepage, index.md, to use the base.njk layout by adding the following as front matter. This should be the first thing in index.md, including the dashes:

--- layout: base.njk ---

If you check the compiled HTML in the public directory, you’ll see the output of the cat and dog loops we created are now within the <body> of the base.njk layout.

Next, we’ll add the same front matter to pets.njk to define that it will also use the base.njk layout to leverage the Eleventy concept of layout chaining. This way, the content we place in pets.njk will be wrapped by the HTML boilerplate in base.njk so we don’t have to write out that HTML each and every time.

In order to use the single pets.njk template to render both cat and dog profile data, we’ll use one of the newest Eleventy features called computed data. This will allow us to assign values from the cats and dogs data to the same template variables, as opposed to using if statements or two separate templates (one for cats and one for dogs). The benefit is, once again, to avoid redundancy.

Here’s the update needed in cats.njk, with the same update needed in dogs.njk (substituting cat with dog):

eleventyComputed:   title: "{{ cat.name }}"   petColor: "{{ cat.petColor }}"   favoriteFood: "{{ cat.favoriteFood }}"   favoriteToy: "{{ cat.favoriteToy }}"   photoURL: "{{ cat.photoURL }}"   ownerName: "{{ cat.ownerName }}"   ownerTwitter: "{{ cat.ownerTwitter }}"

Notice that eleventyComputed defines this front matter array key and then uses the alias for accessing values in the cats dataset. Now, for example, we can just use {{ title }} to access a cat’s name and a dog’s name since the template variable is now the same.

We can start by dropping the following code into pets.njk to successfully load cat or dog profile data, depending on the page being viewed:

<img src="{{ photoURL }}" /> <ul>   <li><strong>Name</strong>: {{ title }}</li>   <li><strong>Color</strong>: {{ petColor }}</li>   <li><strong>Favorite Food</strong>: {{ favoriteFood if favoriteFood else 'N/A' }}</li>   <li><strong>Favorite Toy</strong>: {{ favoriteToy if favoriteToy else 'N/A' }}</li> {% if ownerTwitter %}   <li><strong>Owner</strong>: <a href="{{ ownerTwitter }}">{{ ownerName }}</a></li> {% else %}   <li><strong>Owner</strong>: {{ ownerName }}</li> {% endif %} </ul>

The last thing we need to tie this all together is to add layout: pets.njk to the front matter in both cats.njk and dogs.njk.

With Eleventy running, you can now visit an individual pet page and see their profile:

Screenshot of a cat profile page that starts with the cat's name for the heading, followed by the cat's photo, and a list of the cat's details.
Fancy Feast for a fancy cat. 😻

We’re not going into styling in this article, but you can head over to the sample project repo to see how CSS is included.

Let’s deploy this to production!

The site is now in a functional state and can be deployed to a hosting environment! 

As recommended earlier, Netlify is an ideal choice, particularly for a community-driven site, since it can trigger a deployment each time a submission is merged and provide a preview of the submission before sending it for review.

If you choose Netlify, you will want to push your site to a GitHub repo which you can select during the process of adding a site to your Netlify account. We’ll tell Netlify to serve from the public directory and run npm run build when new changes are merged into the main branch.

The sample site includes a netlify.toml file which has the build details and is automatically detected by Netlify in the repo, removing the need to define the details in the new site flow.

Once the initial site is added, visit Settings → Build → Deploy in Netlify. Under Deploy contexts, select “Edit” and update the selection for “Deploy Previews” to “Any pull request against your production branch / branch deploy branches.” Now, for any pull request, a preview URL will be generated with the link being made available directly in the pull request review screen.

Let’s start accepting submissions!

Before we pass Go and collect $ 100, it’s a good idea to revisit the first post and make sure we’re prepared to start taking user submissions. For example, we ought to add community health files to the project if they haven’t already been added. Perhaps the most important thing is to make sure a branch protection rule is in place for the main branch. This means that your approval is required prior to a pull request being merged.

Contributors will need to have a GitHub account. While this may seem like a barrier, it removes some of the anonymity. Depending on the sensitivity of the content, or the target audience, this can actually help vet (get it?) contributors.

Here’s the submission process:

  1. Fork the website repository.
  2. Clone the fork to a local machine or use the GitHub web interface for the remaining steps.
  3. Create a unique .json file within src/pets/cats or src/pets/dogs that contains required data.
  4. Commit the changes if they’re made on a clone, or save the file if it was edited in the web interface.
  5. Open a pull request back to the main repository.
  6. (Optional) Review the Netlify deploy preview to verify information appears as expected.
  7. Merge the changes.
  8. Netlify deploys the new pet to the live site.

A FAQ section is a great place to inform contributors how to create pull request. You can check out an example on Style Stage.

Let’s wrap this up…

What we have is fully functional site that accepts user contributions as submissions to the project repo. It even auto-deploys those contributions for us when they’re merged!

There are many more things we can do with a community-driven site built with Eleventy. For example:

  • Markdown files can be used for the content of an email newsletter sent with Buttondown. Eleventy allows mixing Markdown with Nunjucks or Liquid. So, for example, you can add a Nunjucks for loop to output the latest five pets as links that output in Markdown syntax and get picked up by Buttondown.
  • Auto-generated social media preview images can be made for social network link previews.
  • A commenting system can be added to the mix.
  • Netlify CMS Open Authoring can be used to let folks make submissions with an interface. Check out Chris’ great rundown of how it works.

My Meow vs. BowWow example is available for you to fork on GitHub. You can also view the live preview and, yes, you really can submit your pet to this silly site. 🙂

Best of luck creating a healthy and thriving community!

Article Series:

  1. Preparing for Contributions
  2. Building the Site (You are here!)

The post A Community-Driven Site with Eleventy: Building the Site appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

A Community-Driven Site with Eleventy: Preparing for Contributions

I’ve recently found myself reaching for Eleventy (aka 11ty) above all other tools when I want to develop a website. It’s hard to beat a static site generator that provides advanced templating opportunities while otherwise getting out of your way and allowing you to just create.

One of those sites is Style Stage, a modern CSS showcase styled by community contributions. Eleventy was perfect for this community-driven project in several ways:

  • Its exceptionally fast builds locally and on a production host
  • It’s un-opinionated about how to construct templates
  • Its ability to create any file type with complete control over how and where files are rendered
  • Its ability to intermix templating languages, such as HTML, Markdown, and Nunjucks
  • It’s highly performant because it compiles to static HTML with no required dependencies for production

The number one reason Eleventy is a great choice for creating a community-driven site is the ability to dynamically create site pages from data sources. We’ll review how to use this feature and more when we create our sample community site.

Article Series:

  1. Preparing for Contributions (You are here!)
  2. Building the Site (Coming tomorrow!)

What goes into creating a community-driven site?

In the not-so-distant past, creating a community-driven site could potentially be a painful process involving CMS nightmares trying to create contributor workflows. Armed with Eleventy and a few other modern tools, this is now nearly fully automatable with a minimum of oversight.

Before we get to inviting contributors, we’ve got some work to do ourselves.

1. Determine what content contributors will have access to modify

This will guide a lot of the other decisions. In the case of using Eleventy for Style Stage, I created a JSON file that contributors can use to create pull requests to modify and provide their own relevant metadata that’s used to create their pages.

An early version of the JSON file which initially had an “Example” for contributors to reference. This screenshot also shows the first two contributors details.

Perhaps you also want to allow access to include additional assets, or maybe it makes sense to have multiple data files for the ease of categorizing and querying data. Or maybe contributors are able to add Markdown files within a particular directory.

Consider the scope of what contributors can modify or submit, and weigh that against an estimate of your availability to review submissions. This will help enable a successful, manageable community.

GitHub actions can make it possible to label or close a pull request with invalid files if you need advanced automated screening of incoming content.

2. Create contributor guidelines

Spending time upfront to think through your guidelines can help with your overall plan. You may identify additional needed features, or items that can be automated.

Once your guidelines are prepared, it’s best to include them in a special file in your GitHub repository called CONTRIBUTING.md. The all-caps filename is the expected format. Having this file creates an automatic extra link for contributors when they are creating their pull request or issues in a prompt that ask them to be sure they’ve reviewed the guidelines:

Screenshot courtesy of the GitHub documentation.

How to handle content licensing and author attribution are things that fall into this category. For example, Style Stage releases contributed stylesheets under the CC BY-NC-SA license but authors retain copyright over original graphics. As part of the build process, the license and author attribution are appended to the styles, and the authors attribution metadata is updated within the style page template.

You’ll also want to consider policies around acceptable content and what would cause submissions to be rejected. Style Stage states that:

Submissions will be rejected for using obscene, excessively violent, or otherwise distasteful imagery, violating the above guidelines, or other reasons at the discretion of the maintainer.

3. Prepare workflow and automations

While Eleventy takes care of the site build, the other key players enabling Style Stage contributions are Netlify and GitHub.

Contributors submit a pull request to the Style Stage repo on GitHub and, when they do, Netlify creates a deploy preview. This allows contributors to verify that their submission works as expected, and saves me time as the maintainer by not having to pull down submissions to ensure they meet the guidelines.

The status of the Netlify deploy updates in real-time on the pull request review page. Once the last item (“/deploy-preview”) displays “Deploy preview ready!” clicking “Details” will launch the live link to the preview.

All discussion takes place through GitHub. This has the added advantage of public accountability which helps dissuade bad actors.

If the contributor needs to make a change, they can update their pull request or request a re-deploy of the branch preview if it’s a remote asset that has changed. This re-deploy is a very small manual step, and it may not be needed for every PR — or even at all, depending on how you accept contributions.

The last step is the final approval of the PR and merging into the main branch. Once the pull request is merged, Netlify immediately deploys the changes to production.

Eleventy is, of course, a static site generator, and several hosts offer webhooks to trigger a build. Netlify’s build plugins are a good example of that. But if you need to refresh data more often than each time a PR is merged, one option is to use IFTTT or Zapier to set up daily deploys, or deploys based on a variety of other triggers.

Example of completed setup of a daily deploy via webhook from IFTTT

It’s worth noting that what we’re talking about here does limit your contributor audience to having a GitHub account. However, GitHub contributions can be done entirely via the web interface, so it’s very possible to provide guidance so that other users — even those who don’t code — can still participate.

4. Choose a method for contributor and community updates

The first consideration here is to decide how critical it is for contributors to know about updates to your site by evaluating the likely impact of the change.

In the case of Style Stage, the core will be unchanging, but there is some planned optional functionality. I went with a weekly(-ish) newsletter. That way, it is something folks can opt into and there is value for contributors and users alike.

Matthew Ström’s “Using Netlify Forms and Netlify Functions to Build an Email Sign-Up Widget” is a great place to learn how to add subscribers to your newsletter with a static form in Eleventy. It also covers a function for sending the subscriber’s email to Buttondown, a lightweight email service. For an example of how to manage your Buttondown email template and content in Eleventy, review the Style Stage setup which shows how to exclude the newsletter from the published site build.

If you’re only expecting low priority updates, then GitHub’s repo notifications might be sufficient for communication. Creating releases is another way to go. Or, hey, it’s even possible to to incorporate notifications on the site itself.

5. Find and engage with potential contributors

Style Stage was an idea that I vetted by tossing out a poll on Twitter. I then put out a “call for contributors” and engaged with responders as well as those who retweeted me. A short timeline also helped find motivated contributors who helped Style Stage avoid launching without any submissions. Many of those contributors became evangelists that introduced Style Stage to even more people. I also promoted a launch livestream which doubled as promotional material.

This is what it means to “engage” with contributors. Creating avenues for engagement and staying engaged with them helps turn casual contributors into “fans” who encourage others to participate.

Remember that the site content is a great place to encourage participation! Style Stage dedicates its entire page to encouraging submissions. If that’s not possible for you, then you might consider using prompts for contributions where it makes sense.

6. Finalize repo settings and include community health files

Finally, ensure that your repository is published publicly and that it includes applicable “community health” files. These are meant to be documents that help establish guidelines, set good expectations with community members, define a code of conduct, and other information that contribute to the overall “health” of the community. There are a bunch of examples, suggestions and tips on how to do this in the GitHub docs.

While there are a half dozen files noted in the documentation, in my experience so far, the three files you’ll need at minimum are:

  • a README.md file at the root of the project that includes the project’s name and a good description of what it is. GitHub will display the contents below the list of files in the repo.
  • a CONTRIBUTING.md file that describes the submission process for contributions. Be explicit as far as what steps are involved and what constitutes a “good” submission.
  • a pull request template. I wouldn’t exactly say this is a mandatory thing, but it’s worth adding to this list because it further solidifies the expectations for submitting contributions. Many templates will even include a checklist that details requirements for approval.

Oh, and having a branch protection rule on the main branch is another good idea. You can do this by going to SettingsBranches from the repo and selecting the “Add rule” option. “Require pull request reviews before merging” and “Require review from Code Owners” are the two key settings to enable. You can check the GitHub docs to learn more about this protection.

Coming up next…

What we covered here is a starting point for creating a community-driven site with Eleventy. The point is that there are several things that need to be considered before we jump straight into code. Communities need care and that requires a few steps that help establish an engaged and healthy community.

You’re probably getting anxious to start coding a community site with Eleventy! Well, that’s coming up in the next installment of this two-parter.  Together, we’ll develop an Eleventy starter from scratch that you can extend for your own community (or personal) site.

Article Series:

  1. Preparing for Contributions (You are here!)
  2. Building the Site (Coming tomorrow!)

The post A Community-Driven Site with Eleventy: Preparing for Contributions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,