Building a Blog:

Howdy!

They say the best time to start a technical blog is twenty years ago, and that the second best time is today. Continue reading to learn about my site and how I built it.


Contents


Humble Beginnings

I really decided on building a blog when I started working on Peter Shirley’s Ray Tracing in One Weekend series. As excellent as the content is, some of the explanations and illustrations are a bit muddy. Searching for additional resources led me to Victor Li’s Blog. Inspired by the clarity, variety, and layout of Victor’s blog, I constructed a similar site for myself to document my work and as a developer.


GitHub Pages

As you may have surmised from the URL, this site is hosted by GitHub Pages. Originally, I was going to register a domain from Hostinger, but GitHub Pages is FREE. Additionally, GitHub is probably the most sensible place to expand my portfolio, and a static site is all I really needed for a technical blog… for now.

The only real trouble I ran into was trying to use custom plugins. More on this issue and how I went about solving it below.


Jekyll

Straight from Jekyll’s GitHub page…

Jekyll is a simple, blog-aware, static site generator perfect for personal, project, or organization sites. Think of it like a file-based CMS, without all the complexity. Jekyll takes your content, renders Markdown and Liquid templates, and spits out a complete, static website ready to be served by Apache, Nginx or another web server. Jekyll is the engine behind GitHub Pages, which you can use to host sites right from your GitHub repositories.

For the most part, Jekyll has been a breeze to work with, and their documentation is top-notch. I would recommend it to anyone looking to build a static site.


Custom Ruby Plugins

When constructing the site, this is where I ran into the most trouble. It turns out that when using the github-pages gem, the site is generated in safe mode and the plugins directory is a random string. Additionally, only a few Jekyll plugins are whitelisted by GitHub Pages. When constructing the tag feature, tags worked fine locally, but when pushing to the master branch, the live site had broken elements. Same with the archive page.

One solution is to build the site locally and push the generated site files to GitHub. In this way, GitHub would interpret the site as static files and not as a Jekyll project - skipping the build process. However, doing so would require a lot of manual file management and would be prone to human error.

The better solution, of course, is to automate. Big thanks to Josh Frankel and his post detailing the process.

Update:

Since June 15th, 2021, the building on travis-ci.org is ceased. Please use travis-ci.com from now on.

This includes a brand new subscription fee! And re-configuring!
I don’t feel like dealing with that (or looking into other options right now), so for the time being, I’ll just be jekyll build-ing source and pushing the updated directory _site to master.</span>

Update(Sep 2022): I wrote a little bash script to update the live site from the source branch. Git worktree is a neat feature! I’ve never used it before. Here’s the script:


#!/bin/bash
 
 
if [ $(basename $PWD) != eldun.github.io ]
then
    exit "Please execute 'update-live-site.sh' from the site's root directory"
fi
 
git checkout source
 
# Generate site from branch 'source'
bundle exec jekyll build

# Create a add directory 'live-site' which is essentially branch 'master'
git worktree add live-site master
 
# Move all generated files in _site to root directory of live site (mv doesn't have a recursive option, so I'm using cp)
cp -r _site/* live-site
rm -r _site

cd live-site
git add *
git commit -m "Update live site from branch 'source'"
git push

cd ..
git worktree remove live-site/

</span>

The basic idea is as follows: Workflow for using custom plugins on GitHub Pages


The Travis CI build should start, complete, and the site will be live!

WOW! Quite a bit of work for some custom plugins! Namely, my archive page and tag system. Again, HUGE thanks to Josh Frankel for his post!


Archive Page

The archive page I have on my site at the time of this post is adapted from Sodaware’s Repository. There’s not much to it, just some basic ruby data collection and a simple layout file. I would eventually like to style it in a way that is more pleasing on mobile devices.


Tag System

For post tagging, I followed an example from Lunar Logic. In the Lunar Logic post, the author, Anna Ślimak, details using Jekyll hooks - which allow for fine-grained control over various aspects of the build process. For example, one could execute custom functionality every time Jekyll renders a post. That’s exactly what I’m doing on my site for the tags.

While tags can simply be entered in the Front Matter of posts, no html is generated for that specific tag. I could manually create a file for said tag in the tags directory, but the hook automatically does that work for me.

Here’s the code:

Jekyll::Hooks.register :posts, :post_write do |post|
    all_existing_tags = Dir.entries("tags")
      .map { |t| t.match(/(.*).md/) }
      .compact.map { |m| m[1] }
  
    tags = post['tags'].reject { |t| t.empty? }
    tags.each do |tag|
      generate_tag_file(tag) if !all_existing_tags.include?(tag)
    end
  end
  
  def generate_tag_file(tag)
    File.open("tags/#{tag}.md", "wb") do |file|
      file << "---\nlayout: tag-page\ntag: #{tag}\n---\n"
    end
  end

Search Function

The search function was adapted from Christian Fei’s Simple Jekyll Search. Here’s the rundown:

Jekyll is all client-side, so the required content for a search must be stored in a file on the site itself.

Within the root of the Jekyll project, a .json file is created from existing posts containing data to search through:

search.json:

---
---
[
  {% for post in site.posts %}
    {

      "postTitle"       : "{{ post.title | escape }}",
      "postSubtitle"    : "{{ post.subtitle | escape }}",
      "postTags"        : "{{ post.tags | join: ', ' }}",
      "postDate"        : "{{ post.date }}",
      "postUrl"         : "{{ post.url }}"

    } {% unless forloop.last %},{% endunless %}
  {% endfor %}
]

This code generates a search.json file in the _site directory. Don’t forget to add escape characters to prevent the .json file from getting messed up. Liquid has some useful filters that can help out. Here’s a snippet of my generated search.json:

...
    {

      "postTitle"       : "Building a Blog:",
      "postSubtitle"    : "Howdy!",
      "postTags"        : "web, ruby",
      "postDate"        : "2020-05-18 00:00:00 -0400",
      "postUrl"         : "/2020/05/18/building-a-blog.html"

    } ,
...

If we wanted to include other aspects of the post in our search, such as the excerpt, content, or custom variables, we could easily follow the template above.

Save the search script in /js/simple-jekyll-search.js.

I placed the necessary HTML elements for the search function inside /_includes/search-bar.html:

<!-- Html Elements for Search -->
<div id="search-container" style="visibility: hidden;">
  <input type="text" id="search-input" placeholder="Search..." />
  <ul id="results-container"></ul>
</div>

<!-- Script pointing to search-script.js -->
<script src="/js/site-scripts/search-script.js" type="text/javascript"></script>

<!-- Configuration -->
<script>
  SimpleJekyllSearch({
    searchInput: document.getElementById('search-input'),
    resultsContainer: document.getElementById('results-container'),
    json: '/search.json',
    searchResultTemplate: '<li><a href="eldun.github.io{postUrl}">{postTitle} {postSubtitle}</a></li>'
  })</script>

and included it right below the nav bar in /_layouts/default.html.

The searchResultTemplate variable above determines what is included in the dropdown search results.

Lastly, I added some javascript at /js/site-scripts/toggle-search.js to toggle the search bar from visible to hidden:


 function toggleSearch() {
    var search_container = document.getElementById("search-container");
    var search_button = document.getElementById("search-button");
    var search_input = document.getElementById("search-input");
    var results_container = document.getElementById("results-container")


    if (search_container.style.visibility == "hidden") {
      search_container.style.visibility = "visible";
      search_input.value = "";
      results_container.style.visibility = "hidden";
      search_input.focus();

    search_input.oninput = handleInput;


      document.onkeydown = function(evt) {
        evt = evt || window.event;

        if (search_input.value == "") {
          results_container.style.visibility = "hidden";
        }
        else {
          results_container.style.visibility = "visible";
        }

        if (evt.keyCode == 27 && search_container.style.visibility == "visible") {
          toggleSearch();
          return;
        }

    };

    function handleInput(e) {
      if (search_input.value != "") {
        results_container.style.visibility = "visible";
      }
      else {
        results_container.style.visibility = "hidden";
      }
    }

    } else {
      search_container.style.visibility = "hidden";
    }

    // Styling
    if (search_container.style.visibility == "visible"){
      search_button.style.color = "#42cad1"
      search_container.style.display = "block";
      // search_input.focus();
    }
    else {
      search_button.style.color = "#cc773f";
      search_container.style.display = "none";
    }
  }

 

The Future

From here onwards, I plan to document as concisely and as compellingly as possible every personal project I undertake.

Oh, and to reformat my style.css. It’s a little sloppy.


Updates

Refactored my CSS into multiple files
I used to have one monolithic `style.scss` file. Thanks to [Sass](https://sass-lang.com/), variables, and some refactoring, this is the result:
eldun.github.io/
    ├── assets/
    │   ├── css/
    │   │   └── style.scss
    │   ├── images/
    │   └── webfonts/
    ├── _sass/
    │   ├── fontawesome/
    │   └── site/
    │       ├── about-me.scss
    │       ├── archive.scss
    │       ├── header.scss
    │       ├── images.scss
    │       ├── mathjax.scss
    │       ├── nav.scss
    │       ├── post-navigation.scss
    │       ├── posts.scss
    │       ├── search.scss
    │       └── tags.scss
    └── ...

`style.scss` is now mostly imports and high-level stylings:

---
---
$color-primary: {{ site.data.colors["primary"]["dark-theme"] }};
$color-secondary:  {{ site.data.colors["secondary"]["dark-theme"] }};
$color-accent: {{ site.data.colors["accent"]["dark-theme"] }};
$color-clickable: {{ site.data.colors["clickable"]["dark-theme"] }};
$color-text: {{ site.data.colors["text"]["dark-theme"] }};

/* This file extends/overrides the CSS file used by "jekyll-theme-cayman" (_site/assets/css/style.css)
  https://help.github.com/en/github/working-with-github-pages/adding-a-theme-to-your-github-pages-site-using-jekyll#customizing-your-themes-css 
*/


// I took this out for a second, but putting it back in is easier than writing a bunch of responive css
@import "jekyll-theme-cayman";

// The default folder for scss files is _sass (this can be changed in config.yml)
// Was having a lot of trouble trying to use fontawesome icons with their relative paths
// before creating the_sass directory and moving the scss files there.
@import "fontawesome/fontawesome.scss";
@import "fontawesome/solid.scss";
@import "fontawesome/brands.scss";
@import "fontawesome/regular.scss";


@import "site/header.scss";
@import "site/nav.scss";

@import "site/posts.scss";
@import "site/post-navigation.scss";
@import "site/images.scss";

@import "site/mathjax.scss";

@import "site/about-me.scss";
@import "site/archive.scss";
@import "site/tags.scss";
@import "site/search.scss";

// CSS rules here
Started using Prism Syntax highlighter
All I had to do was generate js and css files from Prism's site, plop them into my site directory, and link 'em. To decorate a code snippet with a specific language, all I need to do is specify a code block like so: `<pre><code class="language-xxxx">`
eldun.github.io
    ├── assets/
    │   ├── css/
    │   │   └── style.scss // import generated prism css here
    │   ├── images/
    │   └── webfonts/
    ├── _config.yml
    ├── _data/
    ├── downloads/
    ├── _drafts/
    ├── Gemfile
    ├── Gemfile.lock
    ├── _includes/
    ├── index.md
    ├── js/
    │   ├── post-scripts/
    │   └── site-scripts/
    │       ├── prism.js // generated by prism
    │       ├── search-script.js
    │       ├── toggle-search.js
    │       └── vanilla-back-to-top.min.js
    ├── _layouts/
    │   ├── archive-page.html
    │   ├── default.html // link to generated prism js here 
    │   ├── post.html
    │   └── tag-page.html
    ├── _plugins/
    ├── _posts/
    ├── Rakefile
    ├── _sass/
    │   ├── fontawesome/
    │   └── site/
    │       ├── about-me.scss
    │       ├── archive.scss
    │       ├── header.scss
    │       ├── images.scss
    │       ├── mathjax.scss
    │       ├── nav.scss
    │       ├── post-navigation.scss
    │       ├── posts.scss
    │       ├── prism.scss // generated by prism
    │       ├── search.scss
    │       └── tags.scss
    └── ...

Dynamic (& prettier) Table of Contents
Manual tables of content are time intensive and prone to authoring errors, I've come to find. Thankfully there's a gem for generating a 'toc' dynamically, so I don't have to do too much work. The instructions are as follows: After following these steps, I only need to change a few things in _layouts/post.html`:


<div class="post-header inactive">
<h1 id="post-title">{{ page.title }}</h1>
<h3 id="post-subtitle">{{ page.subtitle }}</h3>
<div class="post-date">
    <i class="fas fa-calendar"></i> <time>{{ page.date | date_to_string }}</time>
</div>
</div>

<img src="{{ page.header-image }}" alt="{{ page.header-image-alt }}" title="{{ post.header-image-description }}">

+ <div class='table-of-contents'>{{ content | toc_only }}</div>

+ {{ content | inject_anchors }}

<hr>
<h1><i class="fas fa-hand-peace"></i></h1>
<div class="post-tags">
    {% include post-tags.html %}
</div>


Now I can just use regular headers instead of clumsy anchors, and my table of contents will be generated automatically.
Add the ability to use Liquid in Front Matter
I've started putting more of my post content into the [Front Matter](https://jekyllrb.com/docs/front-matter/) and letting my layout do most of the work. You can't natively use [Liquid](https://jekyllrb.com/docs/liquid/) in Front Matter, but there's a gem for enabling such functionality.

Hook eldun.github.io up to a custom domain
Looks are everything, sometimes. After spending some cash on a custom domain with mailchimp, I thought I'd be able to simply drag-n-drop my statically generated website into their filesystem and have it just work. Nope! Based on my limited understanding of the internet, I had three options: - Redirect eldun.github.io to eldun.net(which would require rebuilding my site with MailChimp's tools and changing my whole workflow) - Redirect eldun.net to eldun.github.io (better, but still ugly) - Ask for help from MailChimp support I asked for help, and Fernando was able to set up a masked forward from eldun.net to eldun.github.io - showing the content of eldun.github.io, but leaving the URL as eldun.net. This will take effect (hopefully) in the next 48 hours. Oddly enough, the configuration for masked forwarding is hidden from the user in Mailchimp: > (09:00:00 PM) Evan: okay, are these settings available for me to look at within the domain settings? > (09:00:45 PM) Fernando : You would not be able to see that within the domain settings, if you need to make any changes to the forward you would reach out to us directly

#web #ruby