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>
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>
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 }}
Done!
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 thesections/
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 }}
{{ 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 }}
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>
With the new page header, the new About
page now looks like this:
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 }}
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 if
s 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:
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 "" }}
And:
GitHub Pages context file
{{ define basePath "/magnanimous-tutorial" }}
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:
- stylesheet import:
<link rel="stylesheet" href="{{eval basePath}}/css/milligram.min.css">
- section links:
<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!
Part 3 - Publishing the website on GitHub Pages
At this point, you have two ways of building your website:
to deploy locally, you just do as you’ve been doing so far:
$ magnanimous
to build it with the Github project name as the base path, for publication on GitHub Pages:
$ magnanimous -globalctx _github_global_context
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.