11ty: Ways to Debug Data

I’m in the process of migrating this blog from Managed WordPress on GoDaddy to Eleventy, GitHub and Netlify. Since I like the convenience of writing content in WordPress, I decided to continue to use it, but just as a headless CMS. When 11ty builds the site, it would fetch WordPress post, page, category, tag, and author data and, using the eleventy-fetch plugin, cache the data locally for a customizable period (I chose 1 day). Since Netlify automatically triggers a build when it detects a commit or push to GitHub, an automated build only happens when I make changes that are tracked by git and not changes in WordPress. For WordPress changes that I want published, I would manually trigger a build in the Netlify admin panel.

During this migration project, there were coding bugs that needed to be fixed. Following are some of the ways I discovered helped me to debug in Eleventy.

Debugging data in JavaScript files in the _data folder

As mentioned above, my project fetches data from WordPress to dynamically build pages. For example, I have a file at _data/authors.js that fetches data from the WordPress remote API endpoint, does some custom processing, and returns the data. It is often necessary to see if the data at different points of the code is as expected. To see this data, you can simply console out the data variable (see line 53 in the screenshot below).

When 11ty builds the site, the console.log statement will output the data in the console / terminal. However, f you are running the default 11ty build command, depending on how many files are being built (in my case, 11ty wrote 1015 files), the data dump may get truncated or lost in the console output. To remove the noise and status messages in the 11ty output, enable quiet mode. Since I’m on Windows, I use the following command.

npx @11ty/eleventy --serve --incremental --quiet 

Now, the output is much simpler, and I can see the data dump immediately.

Debugging data in template files

If you’d like to view the value of data variables in template files, you can do that by outputting the value and passing it to the log function (see example on line 9 below). This will tell 11ty to output the data to the console / terminal.

Dumping all data to a built page

Viewing data in the console terminal is handy for some situations. But, sometimes you can have a lot of JSON data that you’d like to see. In this case, it can be easier to dump the data to a page that you can view the entirety of in a file or at a URL To do this, first add a filter with the following code to .eleventy.js.

eleventyConfig.addFilter('dump', obj => {
    const getCircularReplacer = () => {
      const seen = new WeakSet();
      return (key, value) => {
        if (typeof value === "object" && value !== null) {
          if (seen.has(value)) {
            return;
          }
          seen.add(value);
        }
        return value;
      };
    };
  
    return JSON.stringify(obj, getCircularReplacer(), 4);
  });

Then, create a file to dump the data. In the example below, I have 2 files.

  • dump-posts.njk (to dump WordPress post data)
  • dump-pages.njk (to dump WordPress page data)
---
permalink: dump-post-data.json
---
{{ readyPosts.all | dump | safe }}
---
permalink: dump-page-data.json
---
{{ pages | dump | safe }}

Now, when 11ty builds the site, two pages are created at the following URLs.

  • http://localhost:8080/dump-page-data.json
  • http://localhost:8080/dump-page-data.json

Assembling a 12′ x 14′ Gazebo

Costco had this 12’x14′ gazebo on sale from $2099 to $1699 if you buy it in store.

The assembly instructions say you need 4 people to assemble it.

It turns out, I was able to assemble it with just 2 people by using the Rockwell RK9034 JawStand XP Work Support Stand.

Assembling the posts and beams was easy but time-consuming. It took one day.

The hard part was assembling the roof. Half a day was spent screwing in the aluminum roof panels. Another half was spent raising the roof up and fastening everything together. In the picture below, we used the Jawstand to hold a 2×4 piece of wood which supported the center peak of the roof. The Jawstand made it easy to adjust the height of the 2×4 since the angle of the roof had to constantly be adjusted to get all 4 roof panels to fit right.

Here’s the end result. Two days. Two people.

Strong, Waterproof Glue: Liquid Nails Fuze-It Max VS Loctite PL Marine

I wanted to find a glue that was simple to use, not too expensive, was very strong, and would maintain its strength in wet conditions. There are many glue options to choose from. I ended up choosing two popular brands: Liquid Nails and Loctite. For each brand, I chose either the strongest option or the option that was designed for wet environments. I didn’t include epoxy in my test because I didn’t want to mix two parts together and apply the mixture using a stick. Below are the two options I chose.

I first glued a piece of 2×4 to a concrete landscape block.

After waiting 1.5 to 2 days to fully cure, I attached each piece of wood to a chain to suspend the wood / concrete block combo in the air.

After 2 days, both adhesives kept the wood attached to the concrete block. I then wanted to see if water would affect the bond. I submerged each test in water without having it touch the bottom of the bucket of water.

LeftL Fuze-it | Right: PL Marine

After 12 hours, the wood pieces were still connected to the concrete blocks. But, after 23 hours, the Liquid Nails Fuze-It MAX lost its strength and the wood piece became disconnected from the concrete block.

Left: Fuze-It MAX, Right: PL Marine

So, the winner is Loctite PL Marine, although if you are gluing something in a dry environment, Liquid Nails Fuze-It MAX is probably sufficient. Unsurprisingly, the Marine adhesive is stronger both in dry and wet environments, including while being completely submerged in water.

How To Fix Open / Close Door Error on Microwave

I have the KitchenAid 1.9 cu. ft. Over the Range Convection Microwave in Stainless Steel with Sensor Cooking Technology microwave (model # KMHC319ESS).

Recently, the microwave would keep giving an error saying I needed to “open / close door” no matter how many times I open and close the door. This prevented me from microwaving anything. Since this was an expensive microwave, I didn’t really want to buy a new one. Fortunately, fixing this error was easy. It turns out that there is a sensor along the inside front bottom edge that detects whether the door is closed or not. I prefer to clean using wet Clorox disinfectant wipes. I find that they lift dirt up easily. I’d then wipe again using a dry paper towel. After doing that, the open/close door error goes away and the microwave can start.

Door sensor along inside bottom edge of microwave
Door sensor along inside bottom edge of microwave door
Cleaning the door sensor
Cleaning the door sensor

Although my microwave is a KitchenAid, apparently this happens to other microwaves as well.

Cable Management

I hate seeing lots of cables everywhere. In this post, I will list various options for managing and hiding cables.

Cord Covers / Cable Raceways

These are useful for hiding cables in a paintable conduit. The cable cover can be made of plastic or metal. Some have an adhesive backing and some you can screw to a wall. They come in various sizes and lengths and include various connectors. These are great for when you can’t hide cables behind or underneath something.

Cable Ties

Cable ties are great for holding multiple cables together. Usually, once you pull to tighten the cable tie, you can’t release it.

If you need to release a cable tie, you can buy the releasable type.

Cable Tie Base Mount

Cable ties will bundle cables together, but oftentimes you’ll need to attach them to a wall or something. You can buy cable ties with a screw hole.

However, you may have a hard time finding these with releasable cable ties. In that case, you can buy the base mounts themselves and insert releasable cable ties in them, or just use a twistie tie.

Cable box

Oftentimes, you’ll have an ugly power strip with a bunch of cables going to it. You can hide this ugliness in a cable box.

Cable Clips

If you need to clip some cables in a removable way, cable clips with adhesive backing can help with that.

Multiple 4K Monitors for Efficient Multitasking and Increased Work Productivity

As a web developer, I absolutely need multiple windows open – maybe more than for people in other professions. Due to the pandemic, like many people, I had to create a home office. I could have used the unused loft upstairs but I didn’t know I’d still be working from home after 2 years and I like being close to the kitchen so I converted the dining table into my work desk. Since I had some old monitors lying around, I used them to have multiple windows open. I thought we’d all just work from home for a month or two max so it didn’t seem worth it to spend money on fancy monitors.

April 2020

As the pandemic progressed, I got a work allowance to buy office equipment for work from home (WFH). I badly needed an ergonomic and comfy work chair. I also got an ultrawide monitor to have even more screen real estate.

This photo does not show the upgraded chair.

I thought about using my 65″ inch LG OLED TV as a monitor but the screen was WAY TOO BIG!

I then read an article about people using 43″ and 50″ TVs with a low input delay as their work monitor and how happy they’ve been. I got the Samsung 50″ ‘The Frame” TV at a 50% open-box discount at BestBuy. As much as it was nice reading code on it, anything on the left and right was uncomfortable to read.

I could have tried the 43″ model of the same TV but it cost more than I wanted to spend. Plus, I wanted more windows open at the same time. Fortunately, BestBuy had some open-box 32″ Samsung UJ59 4K monitors. Regular price: $340. Sale Price: $300. Open box price: $250 and $195 (both looked brand new even though they were open box items). After being tired of a messy dining room / office where I could see the back of my monitors, I decided to rearrange the dining room and get a more suitable desk.

I know what you’re thinking. Why is the laptop halfway off the desk. Since with two 4K monitors, I didn’t think I needed the laptop screen but, how can I resist an extra screen? I’ll get a drawer unit the same height as the desk, put it on the left side of the desk, and put the laptop on it. More importantly: why in the world didn’t I buy two 4K monitors before? This setup makes working so much better I regret not doing this sooner. Now, this setup works for me but you may need to adjust some settings to better suit yours.

Setting up the monitors

Before you go out and buy a bunch of 4K monitors, make sure you laptop / computer supports them. I have a MacBook Pro (2019) and according to Apple, I can run multiple 4K monitors.

My particular Samsung UJ59 monitors don’t have USB-C input – only HDMI – so I have to use a USB-C to HDMI adapter.

Monitor Layout

On MacOS, I chose to have my laptop screen be my main display and my 32″ monitors both be extended displays. You can then drag the squares representing screens to match your physical layout so that when you drag your mouse (I use a trackpad) off the edge of a screen/monitor, the pointer seamlessly appears on the adjacent screen, as you’d expect.

Monitor Resolution

4K screens/TV support a native resolution of 3840 x 2160 pixels. But, you may find that reading code or blog posts difficult when the font is too small. There are two things you can do to address this:

Scale down the resolution

You can have your monitor show a picture at a lower than 4K resolution such that everything appears bigger.

I set my laptop as the main display with a default resolution
The first 4K monitor is set as “Extended display” with a default (4K) resolution
The 2nd 4K monitor is also set as “Extended display” with a default resolution. When I click the “Scaled” option, you can see the default resolution is 3840 x 2160, which is 4K.

Below are lower scaling options.

The scaling option right below 4K is 3360 x 1890 px.
The lowest scaling option is 1920 x 1080 px.

Increase the font size of some or all browser tabs / apps

If you’re in 4K mode and you feel the font is a bit too small to read, you can increase the font size of just that window or browser tab. I use VisualStudio Code as my code editor and I find that increasing the font size makes for a much more pleasant coding experience. Plus, since I have laid out my screen such that VS Code takes up the full height of the screen, I can still see a ton of code without having to scroll a lot.

Conclusion

One survey indicated that slightly more people preferred a single 4K TV as their monitor than people who prefer multiple 4K monitors. This all boils down to your use case. If you don’t need a million windows open like I do, then a single 43″ 4K TV may be better for you (don’t get a 50″ TV – it’s too big if you are sitting 2-3′ from the screen). Otherwise, multiple 4K monitors is definitely the way to go (with some custom setting adjustments to suit your viewing comfort).

Update

The desk in the picture above was from IKEA. It was too wobbly so I replaced it with this Husky one from Home Depot. The side drawer unit is from IKEA.

Keto, Low-Calorie Pasta

People may not realize this but traditional pasta like spaghetti contains an enormous amount of calories, both from the pasta itself and the sauce. Fortunately, there is a healthy, low-calorie alternative, albeit a bit more expensive. Below is a comparison of traditional to healthy pasta. First, we’ll start with healthy pasta which uses HealthyNoodles available at Costco.

tldr;

HealthyNoodles-based Pasta

This pasta comes precooked. Since one serving is a ridiculously small amount, let’s say we make 4 servings (452 grams, 120 calories).

For the sauce, we’ll make our own using tomato sauce from Target.

I find that one 18oz can is a good amount for the amount of pasta above. The sauce comes with some spices but we can add more like Italian seasoning, oregano, etc. There are many recipes online for making pasta sauces from tomato sauce. One can (427 grams) of this sauce has 105 calories.

Traditional Pasta

The calories listed on this package is for dry spaghetti. To compare with HealthyNoodles above, we want the calories for cooked spaghetti having a weight of 452 grams. According to Yazio, that amount is 714 calories.

Now, for the sauce, let’s go with Raos marinara, which is a very popular sauce available at Costco.

125 grams of this sauce has 90 calories. Therefore, 427 grams contains 307 calories.

Comparison

Now that we have our values, let’s compare the total calories from each type of pasta.

Traditional PastaHealthy Pasta
Noodles714120
Sauce427105
Total Calories1141225

Conclusion

For the amount specified above, Healthy pasta is 916 calories LESS than the calories in traditional pasta. That’s 80% less calories!

Generate a Static Website with 11ty (Eleventy)

This post will describe how to create a simple website with the NodeJS-based static site generator (SSG), 11ty (Eleventy). According to this article, 11ty is the 3rd fastest SSG.

I’ve tried Hugo. It’s definitely fast and easy to install. But, I found that it wasn’t flexible in how it could be used and not as intuitive as Jekyll and 11ty. I’ve also tried Jekyll. It was a bit tricky to install on Windows. It would sometimes get stuck or not detect file changes and therefore not build and reload them in the browser. LiveReload didn’t work, for me at least. 11ty was super easy to install, as you’ll see below. The build time definitely goes up with the number of pages. It’s super flexible and customizable and intuitive to use. And, it supports a bunch of templating engines like HandlebarsJS, Liquid, Nunjucks, EJS, and even plain JavaScript.

Here’s a step-by-step guide to getting started with 11ty to build a simple website from scratch. This tutorial was done on Windows.

Install the latest version of Node JS

https://nodejs.org/

Verify Node JS installation

If you’re on Windows, open the Command Prompt and run node -v to check the version of Node installed.

Optionally, install the latest version of PowerShell

If you’re on Windows, you can use the default command prompt. Or, you can use PowerShell. If you don’t have the latest version of PowerShell, you can download it from here. You can also install Windows Terminal.

Instead of the default command prompt (cmd.exe), I’m going to use PowerShell. Open PowerShell and run node -v to check your version of Node installed. If PowerShell doesn’t find Node, then check that Node is in your path.

Open System Properties

Click Environment Variables

Under System variables, click Path.

If there is no path to the nodejs folder, add it as the last item. 

If it exists but comes before the PowerShell path, then move it down below the PowerShell path.

Close and reopen PowerShell and rerun node -v.

Create website folder

mkdir eleventy-poc
cd eleventy-poc

Create a package.json

npm init -y

Install eleventy and save it in package.json

npm install --save-dev @11ty/eleventy

Run eleventy (verify it runs)

npx @11ty/eleventy

Install HTML 5 boilerplate

We’ll use HTML5 Boilerplate as a basis for our website. Go to https://html5boilerplate.com/ and download the latest version.

Extract and copy all files to the website folder (eleventy-poc) EXCEPT for package.json and package-lock.json. You don’t want to overwrite the 11ty dependencies that were already saved there.

Create an _includes and _layouts folder

Create a new file in the root folder called .eleventy.js. Add to it the following code to 

  • Specify folders that will be copied to the build output folder
  • Specify which folders contain include and layout files

Include files are reusable code snippets like components, e.g. header, footer, etc.

Layout files are files that determine a page’s layout. Layout files can wrap other layout files.

module.exports = function(eleventyConfig) {
    // Output directory: _site
 
    // Copy `css/` to `_site/css`, etc
    eleventyConfig.addPassthroughCopy("css");
    eleventyConfig.addPassthroughCopy("img");
    eleventyConfig.addPassthroughCopy("js");
 
    return {
        dir: {
        // ⚠️ These values are both relative to your input directory.
        includes: "_includes",
        layouts: "_layouts"
        }
    }
};

Update links

The doc folder contains instructions on how to use HTML5 Boilerplate. You may delete it if you want. If you keep it, update the links (search and replace) TOC.md to /doc/TOC/ so that when the markdown files are built as HTML files, the TOC links will work.

Also, in TOC.md, let’s do the same with the local links, e.g. 

  • “usage.md” becomes “/doc/usage/” 
  • “faq.md” becomes “/doc/faq/”
  • etc

Re-run Eleventy when you save 

npx @11ty/eleventy --serve

This runs a local web server and loads BrowserSync to auto-reload the browser when you save. 

Notice in the output below that a _site folder was created containing 10 HTML files that were built and 14 files were copied. The files that were copied were from the css, js, and img folders as specified in the config file.

The output folder structure looks like this

We can browse to http://localhost:8080/ and see the home page. If we go to http://localhost:8080/doc/TOC/, we’ll see the HMTL5 Boilerplate documentation in HTML format (converted from Markdown).

Create a default layout

At this point, we have our boilerplate home page (index.html) file.

Let’s create a header, footer, and sidebar containing nav links, and a main content area, like most websites have.

<header>
    <p class="logo">ABC Company</p>
</header>
  <div class="content-wrapper">
    <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about/">About</a></li>
        </ul>
    </nav>
    <main>
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore, velit provident sed cum repellendus repudiandae ut mollitia animi. Voluptates expedita corporis pariatur sed quam. Ducimus fugiat quos eum aut eaque?</p>
    </main>
</div>
<footer>
    Copyright 2022
</footer>

I’ll also add some basic CSS to main.css to style the page.

/* ==========================================================================
   Author's custom styles
   ========================================================================== */
header,
footer {
    padding: 1em;
    background-color: #70a9dc;
}
.logo {
  font-size: 2em;
}
.content-wrapper {
  display: flex;
  min-height: 50vh;
}
main {
  padding: 1em;
}
nav {
  background-color: #efefef;
  padding-right: 3em;
}

Now, 11ty (BrowserSync) will auto-reload the page and we’ll see

Let’s create a layout from this index.html page.

  • Copy index.html to _layouts/default.njk (this will be a Nunjucks template)

In _layouts/default.njk

  • Prefix all relative paths with “/”, e.g.
    • css/normalize.css becomes /css/normalize.css
    • js/main.js becomes /js/main.js
  • Copy the <head> code block to _includes/head.html
  • Copy the <header> code block to _includes/header.html
  • Copy the <nav> code block to _includes/nav.html
  • Copy the <footer> code block to _includes/footer.html
  • Copy all of the <script> tags to _includes/scripts.html
  • Replace the <head> code block with {% include "head.html" %} to include the head code
  • Replace the <header> code block with {% include "header.html" %} to include the header code
  • Replace the <nav> code block with {% include "nav.html" %} to include the nav code
  • Replace the <footer> code block with {% include "footer.html" %} to include the footer code
  • Replace the <script> code with {% include "script.html" %} to include the scripts
  • Replace the lorem ipsum text with {{ content | safe }}. This content variable will be replaced with content from pages that use this template, like the home page.

This is how the files should look.

The contents of default.njk should look like this.

<!doctype html>
<html class="no-js" lang="">

{% include "head.html" %}

<body>
  {% include "header.html" %}
  <div class="content-wrapper">
    {% include "nav.html" %}
    <main>
        {{ content | safe }}
    </main>
  </div>
  {% include "footer.html" %}
  {% include "scripts.html" %}
</body>

</html>

Replace the contents of index.html with the following.

---
layout: default.njk
---
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Facilis, voluptates distinctio fuga culpa praesentium quis repellat, mollitia vel repudiandae suscipit officiis tempora atque ratione quidem quo facere reiciendis, iste sit!</p>

The first section between the triple dashes is the front matter. Here, we tell 11ty to use the default.njk layout for this page. 

The content after the front matter will go where the content variable
{{ content | safe }} is in the layout.

If we look at the home page in a browser, we’ll see that it looks exactly the same as before, except we’ve templatized the home page using a layout and include files. 

Create another page

Let’s create an About page. We have 2 ways to do this:

Method 1: 

Create a file at /about.html.

11ty will convert this to a page at /about/index.html.

Note that any file that is named other than index.html will have a folder created with the same name as the file, e.g. abc.html becomes /abc/index.html.

Method 2: 

Create a file at /about/index.html.

The content of each file can be

---
layout: default.njk
Title: About
---
<h1>{{ title }} </h1>
<p>This is the about page.</p>

In this case, we added a front matter variable called title and output it using curly bracket notation. 11ty is processing these files as Liquid files using the Liquid templating engine.

If we go to http://localhost:8080/about/, we see:

You can also rename about.html to about.njk to use the Nunjucks templating engine. 11ty will process the file according, as you can see in the console.

11ty supports many templating languages including HTML, Markdown, JavaScript, Nunjucks, Liquid, Handlebars, Moustache, EJS (Embedded JavaScript), HAML, Pug, and custom.

Note: You can only choose one method above. If you do both, you’ll get a conflict error, understandably.

Add files for git to ignore

There are some files/folders we don’t want git to track. The HTML5 boilerplate already includes a .gitignore file. Edit it and add “_site” to the list like this. We don’t want to commit the built files to git. Files will be built during deployment.

# Include your project-specific ignores in this file
# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
# Useful .gitignore templates: https://github.com/github/gitignore
node_modules
dist
.cache
_site

Add the local repo to GitHub

We’ll follow these instructions on how to add our local repo to GitHub.

Create a new repository on GitHub.com. To avoid errors, do not initialize the new repository with README, license, or gitignore files. You can add these files after your project has been pushed to GitHub.

Initialize the local directory as a git repo. Run git init -b main in our project folder. This will create a hidden .git folder.

Add the files in your new local repository. This stages them for the first commit.

git add .

Commit the files that you’ve staged in your local repository.

git commit -m "First commit"

At the top of your repository on GitHub.com’s Quick Setup page, click to copy the remote repository URL.

In the Command prompt, add the URL for the remote repository where your local repository will be pushed.

$ git remote add origin  <REMOTE_URL> 
# Sets the new remote
$ git remote -v
# Verifies the new remote URL

Push the changes in your local repository to GitHub.com.

git push origin main

Go to your repo on GitHub and see your files there.

https://github.com/javanigus/eleventy-basic-site

Host site on Netlify

Now, let’s host our site for free on Netlify. Create a Netlify account and add a new site by importing an existing project.

Connect Netlify to GitHub and choose a repo. If you don’t see your repo, configure Netlify to be able to access some or all repos in your GitHub account.

Netlify will analyze your site and suggest default build settings. As you can see below, Netlify correctly detected that my site was built using 11ty so it populated the “Build command” and “Publish directory” using the default Eleventy values.

Click “Deploy site’ and wait a minute or two for your site to be deployed. Netlify will clone the repo, install any dependencies as listed in the package-lock.json file, run the Eleventy build command, and host the built files. You will then see how long it took to build the entire site and be provided a free Netlify URL where you can view the live site. In this case, the entire build took 37s.

The free Netlify URL generated is https://enchanting-malabi-4c2a4e.netlify.app/

Going to that URL, we see the same site as we saw on our local dev machine.

Set Up a Jamstack Develop, Build, and Release System Using GitHub, Hugo, and Netlify

Jamstack is the new standard architecture for the web. Using Git workflows and modern build tools, pre-rendered content is served to a CDN and made dynamic through APIs and serverless functions. Technologies in the stack include JavaScript frameworks, Static Site Generators, Headless CMSs, and CDNs.

Traditional Web (left) vs Jamstack (right)

Jamstack benefits

Jamstack offers many benefits over traditional web stack.

  • Security
    Published text files are HTML, CSS and JavaScript only. No server-side scripts like PHP
  • Scalability
    As website traffic grows, scaling is much easier when your website amounts to a bunch of static HTML files that can be served from a CDN.
  • Performance
    Static HTML files don’t require server-side processing. Files can be served from a CDN.
  • Maintainability
    It’s much easier to maintain a simple server that hosts static files rather than application and database servers. The heavy lifting of building the static files is done before deployment resulting in stable production files.
  • Portability
    Since Jamstack sites are pre-generated, the production files are simple static files which can be hosted anywhere on any simple host.
  • Developer experience
    Jamstack sites are built on widely available tools and conventions making it easier for developers to learn and develop.

Learn more

Jamstack is a term that was coined by two developers who pioneered the architecture while working at Netlify. This post will explain how to set up a web development, build and release system using the following components

  • Hugo (static site generator)
  • GitHub (version control)
  • Netlify (CI/CD + serverless web hosting)
  • Netlify CMS (content management system)
  • imageKit.io (image optimization and CDN)

Web development workflow

Let’s say you have a website at example.com. This workflow will depend on having a GitHub repo with 2 branches (main and develop) and 2 sites hosted on Netlify, one for production (www.example.com) and one for staging (staging.example.com). The staging site will have site-wide password protection using an option in Netlify.

The workflow using this setup is very common:

  1. Edit web page files locally, e.g. using VisualStudio Code
  2. Commit and push changes to develop branch in private GitHub repo
  3. Any commit to the develop branch in GitHub auto-triggers a build to generate and deploy static files to a staging site in Netlify (staging.example.com)
  4. Preview changes in a password-protected, external staging URL
  5. Merge and push changes from develop branch to main branch. This will auto-trigger a build to generate and deploy static files to a production site in Netlify
  6. View published changes in production (www.example.com)

Note

  • If you need to make an edit, you can make it directly in GitHub as well.
  • If you’d like to provide a CMS for your pages (usually for non-technical people), you can use Netlify CMS.

Create GitHub repository and branches

Let’s first create a GitHub repo. We’ll call it example.com and make it private since we don’t want competitors seeing our potentially confidential information. Within this repo, we’ll have 2 branches.

  • main (for www.example.com / production)
  • develop (for staging.example.com / development)

GitHub’s default branch is called main so we’ll use that name for the production branch.

Install Hugo

Since I’m writing this post on a Windows 11 laptop, I will follow the instructions here. For other OSs, follow the instructions here.

  1. Create some folders (C:\Hugo\Sites and C:\Hugo\bin)
  2. Download the Windows executable from the Hugo Releases page (in my case, I’ll go with the extended version to make sure I have all the features I may need – hugo_extended_0.97.3_Windows-64bit.zip)
  3. Extract the executable to C:\Hugo\bin. You will end up with 3 files like this.
  1. Add the executable to the PATH
    1. open Windows PowerShell
    2. change to the C:\Hugo\bin folder
    3. append C:\Hugo\bin to the existing PATH by entering
      $Env:PATH += ";C:\Hugo\bin"
  1. Verify the update by outputting the PATH value using the command
    Write-Output $Env:PATH
  1. Verify the executable by running the command hugo help. You should see output like below.

Create a new site

Now that Hugo is installed, we can use it to create a new site. Run the following commands.

cd C:\Hugo\Sites
hugo new site example.com

You should then see output indicating a new site was created.

In the Hugo/Sites folder, you should see files and folders as shown below.

Here’s what the folders are for.

├── archetypes (templates for different content types)
├── config.toml (top level configuration file)
├── content (this is where content goes in HTML or markdown format)
├── data (for local and remote dynamic content, e.g. JSON data)
├── layouts (layouts for pages (list pages, home page) and partials, etc)
├── static (images, CSS, JavaScript, etc)
└── themes (for themes)

Install a theme

Now, you can browse themes, install one and make customizations to it. In my case, I will create a theme from scratch.

Create a new theme

Run the command hugo new theme exampleTheme to create a theme called “exampleTheme”.

C:\Hugo\Sites\example.com> hugo new theme exampleTheme
Creating theme at C:\Hugo\Sites\example.com\themes\exampleTheme
C:\Hugo\Sites\example.com>

This will create an exampleTheme subfolder in the themes folder. The folder structure should look like this:

.
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── resources
│   └── _gen
│       ├── assets
│       └── images
├── static
└── themes
    └── exampleTheme
        ├── LICENSE
        ├── archetypes
        │   └── default.md
        ├── layouts
        │   ├── 404.html
        │   ├── _default
        │   │   ├── baseof.html
        │   │   ├── list.html (for the list page / index of blog posts)
        │   │   └── single.html
        │   ├── index.html (home page)
        │   └── partials
        │       ├── footer.html
        │       ├── head.html
        │       └── header.html
        ├── static
        │   ├── css
        │   └── js
        └── theme.toml

Note the following files:

  • themes/exampleTheme/layouts/index.html
    this is the home page layout
  • themes/exampleTheme/layouts/single.html
    this is the layout for a single page type, e.g. blog post
  • themes/exampleTheme/layouts/list.html
    this is the layout for a list of page types, e.g. blog posts

When you start development, you want to run hugo server to have Hugo detect file changes and reload your local browser for you. In the output below, we see that the local server is at http://localhost:1313. If I go to that URL, I will see a blank screen since I haven’t created any web pages yet.

Notice also in the output that there are some warnings because we’re missing some files. They will be fixed.

Edit config file

I’m using VisualStudio Code to edit files. First, I’ll update the config.toml file by changing the base URL and title. In order to use the new theme we created, we need to reference it in the config file by adding theme = 'exampleTheme' to it.

Most websites have a main menu for navigating from one page to another. The menu links are common to all pages. Let’s define them in the config file by adding the following to it.

[menu]
  [[menu.main]]
    name = "Home"
    url = "/"
    weight = 1
  [[menu.main]]
    name = "Posts"
    url = "/posts/"
    weight = 2
  [[menu.main]]
    name = "Tags"
    url = "/tags/"
    weight = 3

Layouts & partials

If you open the theme’s layouts folder, you’ll see some default layouts and some partials layouts. The base layout (baseof.html) is the parent-most layout. As you can see in the screenshot below, the base layout has

  • some partials
  • a main block

The list (list.html) and single (single.html) layouts will use the base (baseof.html) layout. The contents of the list and single layouts will go in the main block of the base layout.

The base layout includes partials for the

  • head section (head.html)
  • header section (header.html)
  • footer section (footer.html)

of every page. Partials are like include files. They are small, context-aware components that can be used economically to keep your templating DRY. These are standard sections of most websites which is why they’ve been auto-generated. If you don’t want to use a particular section, you can remove it from the base layout or leave it as is as those partial files are empty by default.

Now, let’s fill out the layouts and partials.

head.html partial

Open example.com/themes/exampleTheme/layouts/partials/head.html in a text editor. This will be the place for metadata like the document title, character set, styles, scripts, and other meta information. Paste the following

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="/css/style.css">
    {{ $title := print .Site.Title " | " .Title }}
    {{ if .IsHome }}{{ $title = .Site.Title }}{{ end }}
    <title>{{ $title }}</title>
</head>

There are two CSS files. We can create an empty style.css file and put it at example.com/themes/exampleTheme/static/css.

For the page title, we will show the site title (taken from config.toml) if we are on the home page, otherwise, we’ll show the site title followed by the page title of the page we’re on.

header.html partial

Normally in a website’s header you will find navigation links. Open example.com/themes/exampleTheme/layouts/partials/header.html  and paste the following

<div id="nav-border" class="container">
    <nav id="nav" class="nav justify-content-center">
        {{ range .Site.Menus.main }}
        <a class="nav-link" href="{{ .URL }}">
        {{ $text := print .Name | safeHTML }}
        {{ $text }}
        </a>
        {{ end }}
    </nav>
</div>

Note how the navigation link names and URLs are coming from the config.toml file we updated earlier. The keyword range causes the template to loop over the items in .Site.Menus.main array. Alternatively, you could just hardcode the nav links directly in the header.html file.

footer.html partial

For the footer, let’s just add a basic copyright disclaimer.

<p class="footer text-center">Copyright (c) {{ now.Format "2006"}} Example.com</p>

The current year will be displayed automatically. If you are wondering why “2006”, you can find out more about it here.

script.html partial

Most websites have some JavaScript. The Hugo auto-generated partial files didn’t include a script partial for JavaScript that should be loaded on all pages. Let’s create one at example.com/themes/exampleTheme/layouts/partials/script.html and paste the following code. Later on, we can add other scripts like jQuery to it.

<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js" integrity="sha256-0rguYS0qgS6L4qVzANq4kjxPLtvnp5nn2nB5G1lWRv4=" crossorigin="anonymous"></script>
<script src="script.js"></script>

Like style.css, we’ll also need to add a script.js file and put it at example.com/themes/exampleTheme/static/js.

metadata.html partial

Let’s create one more partial to display metadata about each post, e.g. date and tags. Each blog post will have front matter containing key-value pairs specific to each post, e.g.

---
author: "John Doe"
title: "My First Post"
date: "2006-02-01"
tags: ["foo", "bar"]
---

The keys can be referenced as variables in our new partial. Create a new file example.com/themes/exampleTheme/layouts/partials/metadata.html and paste the following code.

{{ $dateTime := .PublishDate.Format "2006-01-02" }}
{{ $dateFormat := .Site.Params.dateFormat | default "Jan 2, 2006" }}

<time datetime="{{ $dateTime }}">{{ .PublishDate.Format $dateFormat }}</time>
{{ with .Params.tags }}
    {{ range . }}
        {{ $href := print (absURL "tags/") (urlize .) }}
        <a class="btn btn-sm btn-outline-dark tag-btn" href="{{ $href }}">{{ . }}</a>
    {{ end }}
{{ end }}

baseof.html layout

As mentioned earlier, the base layout was auto-generated by Hugo. But, since we created a script.html partial for JavaScript files and code that needs to load on all pages, let’s add that partial to the base layout. Open  example.com/themes/exampleTheme/layouts/_default/baseof.html and add script.html after footer.html so that JavaScript does not block page rendering.

<!DOCTYPE html>
<html>
    {{- partial "head.html" . -}}
    <body>
        {{- partial "header.html" . -}}
        <div id="content">
        {{- block "main" . }}{{- end }}
        </div>
        {{- partial "footer.html" . -}}
        {{- partial "script.html" . -}}
    </body>
</html>

list.html layout

This layout will be used to display a list of blog posts. As explained earlier, the content in this file will define the main block of the baseof.html layout. We’ll create a simple layout for listing blog posts. Open example.com/themes/exampleTheme/layouts/_default/list.html and paste the following code.

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ range .Pages.ByPublishDate.Reverse }}
<p>
    <h3><a class="title" href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
    {{ partial "metadata.html" . }}
    <a class="summary" href="{{ .RelPermalink }}">
        <p>{{ .Summary }}</p>
    </a>
</p>
{{ end }}
{{ end }}

Note how this layout contains a reference to {{ define "main" }} because we are defining the main block of the base layout. We’re referencing .Summary because we only want to show a summary of each blog post.

single.html layout

This layout will be used to display a single page or blog post. As explained earlier, the content in this file will define the main block of the baseof.html layout. We’ll create a simple single-post layout. Open example/themes/exampleTheme/layouts/_default/single.html and copy and paste the code below.

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ partial "metadata.html" . }}
<br><br>
{{ .Content }}
{{ end }}

Notice how we are including the metadata.html partial in this layout. Unlike in the list partial, which references the Summary variable, this single partial references the Content variable because we want to show the entire blog post content.

Home page layout

The home page layout is at example.com/themes/exampleTheme/layouts/index.html. Since we’re loading Bootstrap CSS, we can use the Jumbotron component to render a hero section. Paste the following.

{{ define "main" }}
<div id="home-jumbotron" class="jumbotron text-center">
  <h1 class="title">{{ .Site.Title }}</h1>
</div>
{{ end }}

404 layout

Hugo auto-generated an empty 404.html layout in our theme. Since Hugo comes with a default 404, we can delete this one or customize it, if we want.

Write your first blog post

In a terminal, run this command hugo new posts/my-first-post.md.

When you do that, Hugo creates a file with some default front matter.

The template for creating new page types is in the archetypes folder. By default, there’s only one called default.md. It’s a markdown file.

Open the newly created content file at example.com/content/posts/my-first-post.md and add some tags to the front matter followed by some content in either Markdown or HTML. Hugo automatically takes the first 70 words of your content as its summary and stores it into the .Summary variable. Instead, you can manually define where the summary ends with a <!--more--> divider. Alternatively, you can add a summary to the front matter if you don’t want your summary to be the beginning of your post.

---
title: "My First Post"
date: 2020-01-26T23:11:13Z
draft: true
tags: ["foo", "bar"]
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pellentesque eu tincidunt tortor aliquam nulla facilisi cras fermentum odio. A erat nam at lectus urna duis. 

Sed velit dignissim sodales ut eu sem. Lectus urna duis convallis convallis 
tellus. Diam sit amet nisl suscipit adipiscing bibendum est. Sed felis eget 
velit aliquet sagittis id consectetur. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. Morbi quis commodo odio aenean. Mollis nunc sed id semper risus in hendrerit gravida rutrum.

<!--more-->

Ac ut consequat semper viverra nam. Hac habitasse platea dictumst vestibulum 
rhoncus. Amet porttitor eget dolor morbi non. Justo eget magna fermentum 
iaculis eu non. Id eu nisl nunc mi ipsum faucibus vitae aliquet nec. Aliquam 
id diam maecenas ultricies. Non sodales neque sodales ut etiam. Amet massa 
vitae tortor condimentum lacinia quis. Erat imperdiet sed euismod nisi porta. 

Nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Viverra 
suspendisse potenti nullam ac. Tincidunt id aliquet risus feugiat in. Varius 
quam quisque id diam vel. Egestas erat imperdiet sed euismod nisi. celerisque felis imperdiet proin fermentum leo vel orci porta non. Ut faucibus pulvinar elementum integer. Fermentum odio eu feugiat pretium nibh ipsum consequat nisl.

Viewing the site

Open a terminal and run the following from the root folder of your site

hugo server -D

The -D flag means to include content marked as draft. Alternative, you can change draft: true to draft: false in the front matter. When I ran that command, I got an error in the terminal.

Since I knew Hugo would serve the site at http://localhost:1313, I went there to see what Hugo would show in this case. Fortunately, Hugo also shows a descriptive error in the browser as well.

Apparently, I added a reference to the script.html partial but I forgot to create the partial. After creating the partial, I reran hugo server -D and I get the following output showing no errors.

The output says that the web server is at http://localhost:1313. Navigating there shows the site as shown below.

This is the home page
This is the list view (there’s only one post right now)
This is the single blog post detail view

How to Fix a Frozen Samsung Ice Maker

Do a Google search and you’ll find that many people have problems with the ice maker in their Samsung refrigerator. Of all the things Samsung can make, it’s ridiculous that they can’t even make an ice maker that just works without freezing up every 3 months. I have the 28 cu. ft. Food Showcase 4-Door Flex™ Refrigerator with FlexZone™ in Stainless Steel. I went with the counter-depth version. It’s smaller than the full-depth version but more expensive for some reason.

I have no problem with this fridge but as cool as it may look, the ice maker is a joke! I used up my extended warranty to have a “certified” technician come out 3 or 4 times to fix the ice maker (some of them are just clueless!). Anyway, in every situation, the ice maker stopped working because of ice buildup preventing the ice maker from working. One technician (a Samsung technician) said it was because I wasn’t using the original Samsung (overpriced) water filter. So, I put in an overpriced Samsung original water filter and, unsurprisingly, the ice maker still stopped working after a while – again, because of ice buildup. And, of course, I’m past the extended warranty so I have to fix it myself. If your stupid Samsung ice maker stops working because of ice buildup, here’s how you fix it (until it stops working again in 3-6 months).

Buy a steam cleaner

I bought this Bissel steam cleaner on Amazon. It’s supposed to be used for cleaning but it works well for melting ice that jams your stupid Samsung ice maker.

Melt the ice

Put some water in the steamer, wait till it’s hot, then stick the nozzle up the ice maker opening and pull the trigger. Super hot steam will fill up the ice maker box and begin to melt any ice in there.

After a while, try to pull out the ice box. If it still doesn’t come out, repeat until it does. Once it comes out, you’ll see some ice buildup like in the picture below.

Use the steamer to target the ice buildup until you can remove all the ice.

In the picture below, you can see that most of the ice is no longer stuck. Once you’ve removed all the ice, put the ice box back in and wait a day for new ice to be made.

And that’s how you temporarily fix your stupid Samsung ice maker.