Magnanimous Gopher Magnanimous Documentation

6. Basic Tutorial

This is a basic tutorial of Magnanimous. The intention of this tutorial is to make you able of using Magnanimous to create awesome website in the least amount of time possible, so let’s get right to the action!

At the end of each section, a link to the relevant commit in the GitHub Repo containing the code written during this tutorial will be provided.

Part 1 - Website header, footer and home page

In Part 1, we’ll set up the basic parts of the website. Things like the header and footer most pages will share.

We’ll also start creating the website’s Home Page and get a HTTP server to serve it locally.

1.1 Create the website’s skeleton

Before we can start building actual content, we need to build some scaffolding. This is the stuff that will probably be present in all of your pages, including the main CSS styles, header and footer etc.

First of all, we need a root directory for our project:

$ mkdir my-website
$ cd my-website

Magnanimous expects all of your content to be put into the source directory.

Within that, there should be a processed directory for files that Magnanimous should process, and a static directory for files which should simply be copied as they are.

Files that go into the processed directory can use Magnanimous instructions, which let you process the contents of a file by including other files into it, adding or removing things to it depending on certain conditions, define and use variables to avoid repetition, and many other cool things, as we’ll see later.

But continuing with the website skeleton: we’ll also definitely need some CSS to make the website look great… let’s create a css directory under static to store the stylesheets:

$ mkdir -p source/static/css
$ mkdir -p source/processed

To keep with the minimalistic nature of Magnanimous, let’s use a minimalistic CSS framework, Milligram, which will make it easy for us to create a good looking website at the cost of adding a 2kb gzipped CSS file to our pages, which is about as cheap as it can get!

To get the Milligram CSS file, you can either download it from its CDN or install with NPM:

$ npm install milligram
$ cp node_modules/milligram/dist/milligram.min.css source/static/css

Great! We’ve got our first website file (and we didn’t have to write anything!)… now we need some HTML fragments (incomplete pieces of HTML that will be used by other files to create complete HTML files).

Create a HTML header at source/processed/_header.html with the following contents:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Magnanimous Tutorial Website</title>
    <link rel="stylesheet" href="/css/milligram.min.css">
</head>
<body>
<header id="home" class="header">
<section class="container">
    <h1>Demo Website</h1>
    <p class="description">A Magnanimous demo website</p>
</section>
</header>
source/processed/_header.html

Now, create a footer:

</body>
<footer class="footer">
    <section class="container">
        <p>This is the website footer</p>
        <p>Check out the <a href="https://renatoathaydes.github.io/magnanimous">Magnanimous</a> website.</p>
    </section>
</footer>
</html>
source/processed/_footer.html

This is the last piece we needed to create full HTML pages! Notice how the header and footer fit together, so we should include them both in all our HTML and Markdown pages.

1.2. Create the Home page (index.html)

We’re ready to create the index.html page now!

{{ include _header.html }}
<section class="container">
    <h3>Creating great Websites</h3>
    <p>Isn't this a good looking page?!</p>
</section>
{{ include _footer.html }}
source/processed/index.html

Done!

GitHub Commit

1.3. Build the website

Just run Magnanimous:

$ magnanimous

It will print something like this:

2019/03/09 20:22:14 No global context file defined.
2019/03/09 20:22:14 Creating file target/index.html from source/processed/index.html
2019/03/09 20:22:14 Creating file target/css/milligram.min.css from source/static/css/milligram.min.css
2019/03/09 20:22:14 Magnanimous generated website in 1.766838ms

Don’t worry about the No global context file defined message for now… we’ll see later how to use a global context to support internationalization and deploying the website to different server root paths.

Check out the target directory! Your website is there, ready to be served.

Here’s what it looks like:

1.4. Serve the website locally

To serve the website locally during development, we’ll need a simple HTTP server.

There’s a lot of choices available!

Check also the Getting Started guide section on

Part 2 - Solving common problems

In Part 2, we’ll explore common features of Magnanimous and learn how we can use them to solve the most common problems we’re likely to face when building static websites.

2.1. Adding Navigation

Most websites require some form of navigation tools, like menu bars or tabs, to let users get around the website.

This implies the website has different pages the user can visit! Let’s create a few dummy pages so we can demonstrate how Magnanimous can help.

By the way, this is a good time to switch from HTML to Markdown for content. It’s much easier to write.

There’s a Markdown Guide in the Magnanimous Docs, check it out if you want to know more about Markdown support in Magnanimous.

Create the following files under the source/processed/sections directory:

The directory maps directly to the page URL, so files inside source/processed/sections/ will be served under the sections/ URL path (e.g. http://www.example.com/sections/home.html).

{{ include /processed/_header.html }}
## About

This website demonstrates some [Magnanimous](https://renatoathaydes.github.io/magnanimous/) features.

{{ include /processed/_footer.html }}
source/processed/sections/about.md
{{ include /processed/_header.html }}
## Contact Us

Do not hesitate to contact us!

[Click here](https://renatoathaydes.github.io/magnanimous/) to visit our website.

{{ include /processed/_footer.html }}
source/processed/sections/contact.md

After running magnanimous again, you can visit the new pages directly on their URL:

Notice that markdown pages are automatically converted to HTML by Magnanimous (browsers can only show HTML file).

But users wouldn’t be able to find out about these pages unless you linked to them from the home page.

Without Magnanimous, you’d need to manually maintain links to the sections in the home page. But Magnanimous makes it much easier: the for instruction lets you generate links to all pages in a particular directory automatically.

The for instruction we need to add to the header looks like this:

{{ for section /processed/sections/ }}\
    <a href="{{ eval section }}" class="button">{{ eval section }}</a>
{{ end }}\

The \ at the end of the instruction lines escapes the new line in the resulting file, so there won’t be lots of empty lines in it.

The for section /processed/sections/ line opens a for loop over the files under the /processed/sections directory. Each file is then assigned to a variable named section.

Next, we create HTML links by calling eval section both in the href and the actual text shown to the users. We’ll improve this later… but for now, here’s what the processed/_header.html file should look like at this point:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Magnanimous Tutorial Website</title>
    <link rel="stylesheet" href="/css/milligram.min.css">
</head>
<body>
<header id="home" class="header">
<nav class="navigation">
    <section class="container">
        <a href="/index.html" class="button">Home</a>
        {{ for section /processed/sections/ }}\
        <a href="{{ eval section }}" class="button">{{ eval section }}</a>
        {{ end }}\
    </section>
</nav>
<section class="container">
    <h1>Demo Website</h1>
    <p class="description">A Magnanimous demo website</p>
</section>
</header>
source/processed/_header.html

With the new page header, the new About page now looks like this:

GitHub Commit

2.2 Adding Content Summary

You may have noticed that the section links on the top of the website show the path to each section file, rather than the name of the sections. That’s because Magnanimous doesn’t know what the user-friendly name of each file should be.

But we can tell it! On each section page, we can define a variable called name which can then be used by the header to display the section name, rather than its path:

{{ define name "About" }}

## About

...

Now, the header can refer to the name variable on each file (notice we write eval section.name instead of just eval section for the link’s text):

{{ for section /processed/sections/ }}\
    <a href="{{ eval section }}" class="button">{{ eval section.name }}</a>
{{ end }}\

This feature allows us to create summaries of files very easily… for example, you could put your blog posts under the processed/blog directory, and add a few definitions on top of each file, like this:

{{ define name "My first blog post" }}\
{{ define date "2019-03-10" }}\
{{ define abstract "A short discussion about what I want to write about on my blog" }}\

## My first blog post

### Abstract

{{ eval abstract }}\

...

On the blog summary page, you could then list your blog posts:

<h2>Most recent blog posts:</h2>
<ul> 
{{ for blog_post /processed/blog }}\
    <li>
        <h3><a href="{{ eval blog_post }}">{{ eval blog_post.name }}</a></h3>
        <div>{{ eval blog_post.date  }}</div>
        <div>{{ eval blog_post.abstract }}</div>
    </li>    
{{ end }}\
</ul>

The for instructions supports sorting and limits:

{{ for blog_post (sortBy date reverse limit 10) }}\
...
{{ end }}

GitHub Commit

2.3 Customizing included content

The navigation bar on our current website now correctly displays the names of the sections and links to the right paths. However, it does not display the button for the current page differently from the other buttons, which can be disorientating for users.

To fix that, we need a way to customize the header, so that it applies some extra style on the button for the current page.

To do that, we’ll use the if instruction, which works like this:

<a href="/index.html" 
   class="button{{if name != `Home`}} button-outline{{end}}">Home</a>

Notice the use of ` (back-ticks) inside the class String to avoid confusion with the surrounding double-quotes. Magnanimous allows both back-ticks and double-quotes as String de-markers.

In plain english: If the current page has defined a name variable with a value different from Home, the button-outline will be applied to the button, otherwise, it won’t.

We haven’t defined the name of the Home page yet… to make this work, we need to add this line to the top of the index.html file (it must be defined before the header is included):

{{ define name "Home" }}

Next, we add similar ifs for the section pages (which already have names!), so that now the HTML header will look like this:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Magnanimous Tutorial Website</title>
    <link rel="stylesheet" href="/css/milligram.min.css">
</head>
<body>
<header id="home" class="header">
<nav class="navigation">
    <section class="container">
        <a href="/index.html" 
            class="button{{if name != `Home`}} button-outline{{end}}">Home</a>
        {{ for section /processed/sections/ }}\
        <a href="{{ eval section }}" 
            class="button{{if name != section.name}} button-outline{{end}}">{{ eval section.name }}</a>
        {{ end }}\
    </section>
</nav>
<section class="container">
    <h1>Demo Website</h1>
    <p class="description">A Magnanimous demo website</p>
</section>
</header>

The navigation buttons will now look highlighted only on the page they refer to:

GitHub Commit

2.4 The global context

When you run magnanimous, you’ll see the following message:

2019/03/10 18:49:34 No global context file defined.

This means you have not yet defined a global context file. The global context file is just a processed file which you can use to define global variables that can be used anywhere.

By default, the global context file is located at source/processed/_global_context, but you can set it to another file by using the -globalctx option (the path must be relative to source/processed:

$ magnanimous -globalctx _local_global_context

This may seem like a bad idea, but it’s extremely useful to solve problems like internationalization and making links that work on different environments. In the next couple of sections we’ll see how we can do that.

2.4.1 Supporting multiple base paths

When your website is not going to be served from the root path /, you will run into trouble with absolute links.

That’s because absolute links will point to the server’s root path, but your website will not be there!

For example, if you deploy your website to GitHub Pages (as we’re going to do soon!), the URL to your website will look like this:

https://username.github.io/projectname/

This website itself, by the way, is hosted on Github!! https://renatoathaydes.github.io/magnanimous/.

Notice that the root path is /projectname, so absolute links to a page under source/processed/example/file.html need to look like /projectname/example/file.html, not just /example/file.html as you in your local machine!

To fix this, you can use the global context to define the basePath of your website, which may be different depending on where you deploy it to.

Let’s say we want to be able to both deploy the website locally, as we’ve been doing, and to GitHub Pages, and that our project name on GitHub is magnanimous-tutorial.

Then, we could define the following 2 global context files:

Global context for local deployment: the website will be under the web server's root path
{{ define basePath "" }}
source/processed/_global_context

And:

GitHub Pages context file
{{ define basePath "/magnanimous-tutorial" }}
source/processed/_github_global_context

Now, we need to change every link in the website to take into consideration the basePath variable.

There’s a whole chapter on Paths and Links as it’s easy to confuse the two. But suffice it to say that links are things you want the browser to resolve, and paths are things you want Magnanimous to resolve.

Given this knowledge, it’s easy to understand why things like include instructions work without the base path, but things like links to stylesheets require it.

Every link we’ve added to the demo website so far is in the header page, so it’s pretty easy to change them:

<link rel="stylesheet" href="{{eval basePath}}/css/milligram.min.css">
<a href="{{eval basePath}}/index.html" 
    class="button{{if name != `Home`}} button-outline{{end}}">Home</a>
{{ for section /processed/sections/ }}\
<a href="{{ eval basePath + section }}" 
    class="button{{if name != section.name}} button-outline{{end}}">{{ eval section.name }}</a>
{{ end }}\

And that’s it!

GitHub Commit

Part 3 - Publishing the website on GitHub Pages

At this point, you have two ways of building your website:

Reading the output, you can make sure Magnanimous picked up the right global context file:

2019/03/10 20:23:44 Using global context file: source/processed/_github_global_context
2019/03/10 20:23:44 Creating file target/sections/about.html from source/processed/sections/about.md
2019/03/10 20:23:44 Creating file target/sections/contact.html from source/processed/sections/contact.md
2019/03/10 20:23:44 Creating file target/css/milligram.min.css from source/static/css/milligram.min.css
2019/03/10 20:23:44 Creating file target/index.html from source/processed/index.html
2019/03/10 20:23:44 Magnanimous generated website in 5.581399ms

We are now almost ready to publish the website on GitHub Pages.

Just one last thing: GitHub Pages lets you use the master branch as the deployment branch, but you want only the files in the target directory to be deployed… luckily they also let you configure a directory as the root of your website, but the directory must be called docs for some reason!

So just make sure to move the files there when you want to deploy:

$ mv target docs

And then git push! Your website should now be available online!

Here’s the one we’ve created in this Tutorial!

And of course, all the code written in this Tutorial is available on GitHub.

In the next Tutorial, we’re going to add internationalization to our website (so that users who speak other languages can also read it in their own languages) and learn how to use Magnanimous components and slots, which will allow us to re-use template snippets very effectively.