<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Koa</title>
    <description>The latest articles on DEV Community by Koa (@koabrook).</description>
    <link>https://dev.to/koabrook</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F276637%2F9d1e6509-dad2-4615-bd33-53778595bb59.jpg</url>
      <title>DEV Community: Koa</title>
      <link>https://dev.to/koabrook</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/koabrook"/>
    <language>en</language>
    <item>
      <title>Creating a basic blog with Eleventy and Netlify CMS completely from scratch</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Fri, 24 Apr 2020 23:46:18 +0000</pubDate>
      <link>https://dev.to/koabrook/creating-a-basic-blog-with-eleventy-and-netlify-cms-completely-from-scratch-197e</link>
      <guid>https://dev.to/koabrook/creating-a-basic-blog-with-eleventy-and-netlify-cms-completely-from-scratch-197e</guid>
      <description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=WEApDhZMAL4"&gt;New video version available!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.11ty.dev/"&gt;Eleventy&lt;/a&gt; is a powerful and wonderfully simple static site generator. &lt;a href="https://www.netlifycms.org/"&gt;Netlify CMS&lt;/a&gt; is a simple, open-source content management system that makes adding, editing and deleting posts and other content a breeze. Today I'm going to show you how to create a basic blog using eleventy and Netlify CMS, entirely from scratch- no boilerplates, no templates, no nothing. Be aware that this tutorial will create an absolute bare-bones text blog for you to build upon- I'm not even adding CSS! Let's get to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Setting up
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js and NPM installed&lt;/li&gt;
&lt;li&gt;Git installed&lt;/li&gt;
&lt;li&gt;A Git provider such as GitLab or GitHub&lt;/li&gt;
&lt;li&gt;A free Netlify account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, there're some things we need to get set up before we begin coding. First, create a git repository on your preferred service (I use and recommend &lt;a href="https://gitlab.com/"&gt;GitLab&lt;/a&gt;) and clone the empty repo to your computer.&lt;/p&gt;

&lt;p&gt;Enter the project root with your terminal and install eleventy by typing &lt;code&gt;npm install @11ty/eleventy --save-dev&lt;/code&gt;. Once that is done, install luxon by typing &lt;code&gt;npm install luxon&lt;/code&gt;. Luxon is the only special addition here and will allow us to format readable dates in our posts.&lt;/p&gt;

&lt;p&gt;Create a new folder in the project's root and name it &lt;code&gt;_includes&lt;/code&gt;. This folder will contain templates that our blog will use to render pages. Create another new folder in the root directory called &lt;code&gt;images&lt;/code&gt; and another inside of that called &lt;code&gt;uploads&lt;/code&gt;. Netlify CMS will use this folder to store images uploaded to your site. Create &lt;em&gt;yet another folder&lt;/em&gt; in the root of your project called &lt;code&gt;admin&lt;/code&gt; and finally, a folder in the root named &lt;code&gt;posts&lt;/code&gt;. Once that's done, we're ready to start coding!&lt;/p&gt;

&lt;p&gt;Create a file in the root of the project named &lt;code&gt;.eleventy.js&lt;/code&gt; and write this in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = function(eleventyConfig) {
  eleventyConfig.addPassthroughCopy('images')
  eleventyConfig.addPassthroughCopy('admin')

  const {
    DateTime
  } = require("luxon");

  // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
    eleventyConfig.addFilter('htmlDateString', (dateObj) =&amp;gt; {
      return DateTime.fromJSDate(dateObj, {
        zone: 'utc'
      }).toFormat('yy-MM-dd');
    });

    eleventyConfig.addFilter("readableDate", dateObj =&amp;gt; {
    return DateTime.fromJSDate(dateObj, {
      zone: 'utc'
    }).toFormat("dd-MM-yy");
  });

};

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;eleventy.js&lt;/code&gt; file is a configuration file. The &lt;code&gt;addPassthroughCopy&lt;/code&gt; lines tell Eleventy to include those folders and their contents in the generated site. The rest is just some JavaScript to make the dates in the postslist more readable.&lt;/p&gt;

&lt;p&gt;Now create another folder within &lt;code&gt;_includes&lt;/code&gt; named &lt;code&gt;layouts&lt;/code&gt;. Within that folder we can create a new file called &lt;code&gt;base.njk&lt;/code&gt;. The &lt;code&gt;base&lt;/code&gt; file forms the basis of every page on our website. We'll add add &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; elements and a site header here. Any element that should appear on &lt;em&gt;every single page&lt;/em&gt; should reside in &lt;code&gt;base.njk&lt;/code&gt;. Let's add the following to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Eleventy + Netlify CMS from scratch&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;
      Eleventy + Netlify CMS from scratch
    &amp;lt;/h1&amp;gt;
    {{ content | safe }}
  &amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Like I said: Bare bones. Note the line that says &lt;code&gt;{{ content | safe }}&lt;/code&gt;: this is where page content for other pages will be rendered.&lt;/p&gt;

&lt;p&gt;Now, create a file named &lt;code&gt;index.njk&lt;/code&gt; at the &lt;strong&gt;root of your project&lt;/strong&gt;. This will be your homepage. Add the following to &lt;code&gt;index.njk&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
layout: layouts/base.njk
permalink: /
---
Hello! Welcome to the Eleventy + Netlify from scratch homepage.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our Nunjucks (&lt;code&gt;.njk&lt;/code&gt;) files will accept html, markdown and more. Between the sets of hyphens lies our page's &lt;strong&gt;frontmatter&lt;/strong&gt;. This data tells Eleventy how to handle our page and provides other information as we need it to. &lt;code&gt;layout&lt;/code&gt; tells it which template to use, in this case &lt;code&gt;base.njk&lt;/code&gt; whilst &lt;code&gt;permalink&lt;/code&gt; tells it what the URL for this page will be (in this case, &lt;code&gt;/&lt;/code&gt;, or the homepage). Anything under the hyphens will be used as the main content on the page. You can use text, markdown or html to create the body. In this case I simply use a line of text.&lt;/p&gt;

&lt;p&gt;Now open your terminal back up and run this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx eleventy --serve&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your website doesn't open automatically, browse to &lt;code&gt;localhost:8080&lt;/code&gt; and you should see our homepage!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A47aZCXX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ydbbq89wuhd1wks1prmt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A47aZCXX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ydbbq89wuhd1wks1prmt.png" alt="Your first page!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Blog posts
&lt;/h2&gt;

&lt;p&gt;Let's add a page that will be used for blog posts next. In the &lt;code&gt;_includes/layouts&lt;/code&gt; folder, add a new file called &lt;code&gt;post.njk&lt;/code&gt;. We'll come back to this in a minute, but first let's create a basic blog post. In the &lt;code&gt;posts&lt;/code&gt; folder, create a file called &lt;code&gt;posts.json&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "tags": "posts"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will ensure that every post in this folder has the correct tag attached. Now add a file in the &lt;code&gt;posts&lt;/code&gt; folder named &lt;code&gt;my-first-post.md&lt;/code&gt;. Even though this is a markdown file, we will still add some frontmatter here. Add the following code to &lt;code&gt;my-first-post.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
layout: layouts/post.njk
title: My first post
description: The first post on the Eleventy + Netlify CMS from scratch blog
date: 2020-04-18
featuredImage: /images/uploads/image1.jpeg
---
Hello, here is the body of the post.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's take a look at our frontmatter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;layout&lt;/code&gt; tells this post which template to use, in this case &lt;code&gt;layouts/post.njk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt; is the title of the post&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; is a short description of the post&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;date&lt;/code&gt; is the date the post was written&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;featuredImage&lt;/code&gt; is an image for the post. Drop any image you want into &lt;code&gt;images/uploads&lt;/code&gt; and use its path- mine is &lt;code&gt;image1.jpeg&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, anything underneath the frontmatter will be the blog post content. Note that we won't be creating posts like this in future since the Netlify CMS will take care of it for us!&lt;/p&gt;

&lt;p&gt;Now that our post is ready, lets render it. Add the following to &lt;code&gt;_includes/layouts/post.njk&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
layout: layouts/base.njk
---
&amp;lt;h2&amp;gt;{{ title }}&amp;lt;/h2&amp;gt;
&amp;lt;h3&amp;gt;{{ date | readableDate }}&amp;lt;/h3&amp;gt;
&amp;lt;img src="{{ featuredImage | url }}" alt="{{ featuredImage }}"&amp;gt;&amp;lt;/img&amp;gt;
&amp;lt;p&amp;gt;{{ content | safe }}&amp;lt;/p&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our post page uses the &lt;code&gt;layouts/base.njk&lt;/code&gt; template and renders blog post data in its body. We use double brackets &lt;code&gt;{{  }}&lt;/code&gt; to refer to data in the post's frontmatter. In the case of &lt;code&gt;my-first-post.md&lt;/code&gt;, the &lt;code&gt;{{ title }}&lt;/code&gt; will end up being &lt;code&gt;My first post&lt;/code&gt;, which we provided in the markdown file. Eleventy will create a blog post page for every markdown file in the &lt;code&gt;posts&lt;/code&gt; folder. Neat!&lt;/p&gt;

&lt;p&gt;We now have a working blog, but no way to see the posts. Let's fix that by adding a new file in the &lt;code&gt;_includes&lt;/code&gt; folder called &lt;code&gt;postslist.njk&lt;/code&gt;. This file will use a &lt;code&gt;For Loop&lt;/code&gt; to create a list of all of our posts. Add this to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  {% for post in postslist %}
    &amp;lt;li&amp;gt;
      &amp;lt;strong&amp;gt;
        &amp;lt;a href="{{ post.url | url }}"&amp;gt;{{ post.data.title }}&amp;lt;/a&amp;gt;
      &amp;lt;/strong&amp;gt;
      -
      &amp;lt;time datetime="{{ post.date | htmlDateString }}"&amp;gt;{{ post.date | readableDate }}&amp;lt;/time&amp;gt;
      -
      &amp;lt;span&amp;gt;{{ post.data.description }}&amp;lt;/span&amp;gt;
    &amp;lt;/li&amp;gt;
  {% endfor %}
&amp;lt;/ul&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With the above code, Eleventy searches for every post in a variable called &lt;code&gt;postslist&lt;/code&gt; and renders the title, date and description of each. To create the &lt;code&gt;postslist&lt;/code&gt; variable, create a new file in the root of the project called &lt;code&gt;posts.njk&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
layout: layouts/base.njk
permalink: /posts/
---
{% set postslist = collections.posts %}
{% include "postslist.njk" %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This file populates the &lt;code&gt;postslist&lt;/code&gt; variable with every &lt;code&gt;post&lt;/code&gt; that has the tag &lt;code&gt;posts&lt;/code&gt;. Earlier we created &lt;code&gt;posts.json&lt;/code&gt; to take care of the tagging for us! If all is well, you can now browse to &lt;code&gt;localhost:8080/posts/&lt;/code&gt; to see your list of posts! Feel free to add a couple more posts as &lt;code&gt;.md&lt;/code&gt; files in the &lt;code&gt;posts&lt;/code&gt; folder to see how they show up.&lt;/p&gt;

&lt;p&gt;The very last thing to do with the blog portion of our website is to go back to &lt;code&gt;index.njk&lt;/code&gt; at the project root and add this line at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% set postslist = collections.posts %}
{% include "postslist.njk" %}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;includes&lt;/code&gt; keyword basically embeds the file into the current page- we're rendering &lt;code&gt;postslist.njk&lt;/code&gt; inside the homepage, which now shows your post list as well!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vTpqB2WI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b2r7idrit5spor2bwcjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vTpqB2WI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b2r7idrit5spor2bwcjq.png" alt="The postlist"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xHpSnbFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cj8v3h0ltiyy9ysj7kbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xHpSnbFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cj8v3h0ltiyy9ysj7kbh.png" alt="Our post page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whew! We're done with this part. Go over to your terminal and push the changes to your Git repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Netlify CMS
&lt;/h2&gt;

&lt;p&gt;Now it's time to set up the Netlify CMS. First we'll have to get our website hosted with Netlify.  Login to Netlify and click &lt;code&gt;New site from Git&lt;/code&gt;. Login with your Git provider (I use GitLab) and authorize Netlify to access your repositories. Select your repo from the list. Under &lt;code&gt;Basic build settings&lt;/code&gt;, set the &lt;code&gt;Build command&lt;/code&gt; to &lt;code&gt;npx eleventy&lt;/code&gt; and the &lt;code&gt;Publish directory&lt;/code&gt; to &lt;code&gt;_site&lt;/code&gt;. Click &lt;code&gt;Deploy site&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IUSb5o3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ncumoqf0ico545dkm6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IUSb5o3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ncumoqf0ico545dkm6h.png" alt="Setting up on Netlify"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After your deploy completes, you will see a link (ending with &lt;code&gt;.app&lt;/code&gt;) that will take you to your live blog. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ughVQhu5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x1npbj5vvhzjmkdpecnu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ughVQhu5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x1npbj5vvhzjmkdpecnu.png" alt="The live blog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the menu above your site URL, click &lt;code&gt;Identity&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;On the Identity screen, click &lt;code&gt;Enable Identity&lt;/code&gt;. After it's enabled, click &lt;code&gt;Settings and usage&lt;/code&gt; and find &lt;code&gt;Services&lt;/code&gt;. Click &lt;code&gt;Enable Git Gateway&lt;/code&gt;. Sign in again if prompted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7AFAKvxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zefmp178sl9jklju0v3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7AFAKvxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zefmp178sl9jklju0v3y.png" alt="Identity Options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the above steps we have successfully created a live website with Netlify. We also enabled Netlify's Identity feature which will take care of authentication and accounts for us. Finally, we enabled Git Gateway, which is an API that will let the Netlify CMS commit changes to the git repository without needing to re-authenticate or give anybody access to it directly.&lt;/p&gt;

&lt;p&gt;Back in your project, create &lt;code&gt;index.html&lt;/code&gt; inside the &lt;code&gt;admin&lt;/code&gt; folder. In this file we will add some basic HTML &lt;a href="https://www.netlifycms.org/docs/add-to-your-site/#app-file-structure"&gt;provided by Netlify&lt;/a&gt; which loads the CMS UI and authorization widget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset="utf-8" /&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&amp;gt;
  &amp;lt;title&amp;gt;Content Manager&amp;lt;/title&amp;gt;
  &amp;lt;script src="https://identity.netlify.com/v1/netlify-identity-widget.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;!-- Include the script that builds the page and powers Netlify CMS --&amp;gt;
  &amp;lt;script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create another file in the &lt;code&gt;admin&lt;/code&gt; folder named &lt;code&gt;config.yml&lt;/code&gt;. .YML files rely on accurate indentation. Be sure to use &lt;strong&gt;spaces to indent, not tabs&lt;/strong&gt;! First, we will enable the Git Gateway API by adding the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend:
  name: git-gateway
  branch: master # Branch to update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we'll point to our &lt;code&gt;images/uploads&lt;/code&gt; folder, which is where pictures uploaded from the CMS will be stored, however since Eleventy stores our static site in the &lt;code&gt;_site&lt;/code&gt; folder, we also need to add a public folder too. Add these lines underneath &lt;code&gt;branch: master&lt;/code&gt;, making sure the lines are &lt;strong&gt;not&lt;/strong&gt; indented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;media_folder: "_site/images/uploads"
public_folder: "/images/uploads"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The last thing to do is to create a &lt;strong&gt;collection&lt;/strong&gt; for our posts. We'll use the &lt;code&gt;config.yml&lt;/code&gt; file to create this collection, and &lt;strong&gt;it will match the frontmatter we set in our post markdown files&lt;/strong&gt; earlier. Add this under the &lt;code&gt;media_folder:&lt;/code&gt; line we just created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collections:
 - name: "posts"        #The name of the collection and should match our posts.json file's tag
   label: "Posts"       #The name of the collection on the CMS itself
   folder: "posts"      #The folder that our blog posts will be loaded from and saved to
   create: true             #Tells the CMS that new posts can be created
   slug: "{{year}}-{{month}}-{{day}}-{{slug}}"  #this sets the URL for the post
   fields:
      - {label: "Layout", name: "layout", widget: "hidden", default: "layouts/post.njk"}
      - {label: "Title", name: "title", widget: "string"}
      - {label: "Description", name: "description", widget: "string"}
      - {label: "Date", name: "date", widget: "date", default: ""}
      - {label: "Post Body", name: "body", widget: "markdown"}
      - {label: "Featured Image", name: "featuredImage", widget: "image"}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's explore the &lt;code&gt;fields&lt;/code&gt; section. Notice that each field matches the frontmatter of our &lt;code&gt;my-first-post.md&lt;/code&gt; file. Let's also explore each part of each line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;label&lt;/code&gt; is the name of the field that will appear in the CMS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is the name of the field, it must match the frontmatter exactly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;widget&lt;/code&gt; tells the CMS which widget to display from a selection. &lt;code&gt;string&lt;/code&gt; is a single line of text and others are available, listed &lt;a href="https://www.netlifycms.org/docs/widgets/"&gt;here&lt;/a&gt;. &lt;code&gt;markdown&lt;/code&gt; is generally used for the post content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt; sets a default value for the field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A full explanation of the collections section can be found &lt;a href="https://www.netlifycms.org/docs/add-to-your-site/#collections"&gt;here&lt;/a&gt;. A collection doesn't have to be for posts only- you can add one for individual pages or even individual datasets. That is beyond the scope of this article, however.&lt;/p&gt;

&lt;p&gt;The last thing to do is to return to &lt;code&gt;layouts/base.njk&lt;/code&gt; and add this line and add it before the closing &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;script src="https://identity.netlify.com/v1/netlify-identity-widget.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the same file, add this before your closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    if (window.netlifyIdentity) {
      window.netlifyIdentity.on("init", user =&amp;gt; {
        if (!user) {
          window.netlifyIdentity.on("login", () =&amp;gt; {
            document.location.href = "/admin/";
          });
        }
      });
    }
  &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great news: You're pretty much done! Save, commit your changes and push your project. Wait for the deploy to complete (make sure your commit says &lt;code&gt;Published&lt;/code&gt; in Netlify under &lt;code&gt;Production deploys&lt;/code&gt;) then go to your website's url. Add /admin to the url and you'll be transported to the login screen! Log in and you'll be taken to the Netlify CMS backend where you can create and edit posts and upload media! Please be aware that posts added via the CMS can take a few minutes to appear after you submit them. Netlify will re-deploy your site when a new post is added!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mrwxd6VQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vwlqom5t4ehxw28h3tof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mrwxd6VQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vwlqom5t4ehxw28h3tof.png" alt="The Netlify CMS!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tutorial was a very quick and crude integration of Eleventy and Netlify CMS, however it should help you to understand &lt;em&gt;how&lt;/em&gt; the two connect, rather than using a boilerplate or template blog, which can easily be too cluttered from the get-go. I hope that this tutorial was helpful in showing you how you can build a simple blog with a back-end editor with very little time and effort. I encourage you to play around with frontmatter, different collections and pages. Please reach out to me on Twitter &lt;a class="comment-mentioned-user" href="https://dev.to/koabrook"&gt;@koabrook&lt;/a&gt;
 with any questions or feedback, as well as to show you your attempts!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hx4Gmc8p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ipog6aou1lzgjq6freah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hx4Gmc8p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ipog6aou1lzgjq6freah.png" alt="Eleventy + Netlify CMS Blog from scratch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://11ty.dev"&gt;11ty.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.netlifycms.org/"&gt;Netlify CMS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.netlifycms.org/"&gt;Eleventy Base Blog&lt;/a&gt;: Provided the starting steps for my journey with Eleventy!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>netlify</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Moving web development from Windows to Linux: Key tips? </title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Wed, 15 Jan 2020 00:46:52 +0000</pubDate>
      <link>https://dev.to/koabrook/moving-web-development-from-windows-to-linux-what-should-i-3a8j</link>
      <guid>https://dev.to/koabrook/moving-web-development-from-windows-to-linux-what-should-i-3a8j</guid>
      <description>&lt;p&gt;I've been a Windows user for as long as I've used computers. I've also been a Windows hater for that amount of time. I'm no stranger to Linux and always have a linux machine to hand for various tasks and tinkering but I've never actually moved my workload over to it.&lt;/p&gt;

&lt;p&gt;Well, I've decided it's time to give the big W a big fat L and go full-time with Linux. I'm using a soft distro called Elementary OS which is based on Ubuntu 18.04. I've been tinkering away all day and I'm very happy so far. Now it's time to dive into my web development setup. I've install my usual suspects like node, Atom, a Markdown editor and such, but I'm wondering: &lt;strong&gt;How do I take my Linux webdev station to the next level?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What are some of the key packages, software and tweaks that will send my web development into orbit?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>linux</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Quick-fire tips for making a better web experience for Featurephone users</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Tue, 31 Dec 2019 18:13:15 +0000</pubDate>
      <link>https://dev.to/koabrook/quick-fire-tips-for-making-a-better-web-experience-for-featurephone-users-152m</link>
      <guid>https://dev.to/koabrook/quick-fire-tips-for-making-a-better-web-experience-for-featurephone-users-152m</guid>
      <description>&lt;p&gt;I've recently been exploring the emerging web experience thanks to Kai OS, a new breed of internet-connected feature phone. In emerging markets, hundreds of millions of users are now getting connected to the internet for the first time. Unfortunately the internet is quite a chore to browse on micro-browsers and tiny non-touch screens.&lt;/p&gt;

&lt;p&gt;Right off the bat, I've come up with a few tips to make browsing on a tiny Kai OS device more bearable. These aren't your typical mobile standards!&lt;/p&gt;

&lt;h1&gt;
  
  
  Lose the header and title your page
&lt;/h1&gt;

&lt;p&gt;That's right, my first unusual tip is to totally lose your site's header in favour of the site's title which is shown permanently on the address bar. Perhaps a little extreme, but your logo alone might take up over half the screen in some cases! If you really want to keep a header, try to make it as small as possible, preferable no larger in height than one line of test. Also lose the image and make it text if possible.&lt;/p&gt;

&lt;h1&gt;
  
  
  Turn your hamburger menu back into text-links
&lt;/h1&gt;

&lt;p&gt;The less clicks needed to navigate your site on Kai OS, the better. I propose ditching the hamburger menu on micro-sizes and opting for plaintext links instead. Keep the text small and allow only up to three links per row.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More scrolling is better than more clicks&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Hide images behind links
&lt;/h1&gt;

&lt;p&gt;You want your page to consume as little data as possible to be considerate of data rates in developing countries. Hide your images behind plaintext links and don't load them until they're requested. This also makes your page more scrollable and easier to navigate.&lt;/p&gt;

&lt;h1&gt;
  
  
  No fixed content, ever.
&lt;/h1&gt;

&lt;p&gt;Ditch your fixed header and footer- these will make your site completely, utterly unusable on tiny screens.  Your viewport is already miniscule so every pixel counts!&lt;/p&gt;

&lt;h1&gt;
  
  
  Avoid pop-ups entirely
&lt;/h1&gt;

&lt;p&gt;Pop-ups are the bane of micro-web-browsing. Some websites, even W3Schools, are impossible to navigate because there is a pop-up that obscures the screen. Hopefully, as the OS matures its browser will allow a better mouse-like cursor so we can close pop-ups.&lt;/p&gt;

&lt;h1&gt;
  
  
  Give pop-ups big close buttons
&lt;/h1&gt;

&lt;p&gt;If you can't avoid a pop-up or modal, at least give it a big close button, preferably at the top where it's easy to reach.&lt;/p&gt;

&lt;p&gt;Those are my first  quick-fire tips for creating bearable web experiences on Kai OS and other internet-connected feature-phones. Developing websites that offer a decent experience on these devices is a challenge, but we shouldn't disregard this massive group of new users who are diving into an internet that is quite inaccessibile already.&lt;/p&gt;

</description>
      <category>kaios</category>
      <category>webdev</category>
      <category>ux</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Three inspirational developer websites at 240 x 320 px</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Fri, 27 Dec 2019 19:20:34 +0000</pubDate>
      <link>https://dev.to/koabrook/three-inspirational-developer-websites-at-240-x-320-px-42l1</link>
      <guid>https://dev.to/koabrook/three-inspirational-developer-websites-at-240-x-320-px-42l1</guid>
      <description>&lt;p&gt;A hundred million new internet users are coming online in the developing world thanks to the advent of low-cost, internet-connected featurephones such as those running on KaiOS. For the first time ever, users are taking their first steps into the internet on screens as limited as 240 x 320 resolution. Unfortunately their browsing experience will be marred by &lt;a href="https://dev.to/shemthedev/is-the-future-of-the-web-text-centric-3ck9"&gt;biased standards&lt;/a&gt; of the modern web. As a kind of tribute, I want to highlight five excellent examples of developer websites whose designs scale perfectly down to minimal screen sizes.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a href="https://whizzoe.com/"&gt;Zoe Chew: whizzoe.com&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sSdVy-3q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/oh2a2p9se28crlc04d0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sSdVy-3q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/oh2a2p9se28crlc04d0s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zoe's website is the pinnacle of a modern developer website. Taking a single-column approach, her website scales flawlessly down to our minimum resolution without a single thing out of alignment and not a pixel of horizontal scroll is needed. A text-first, image-lite experience means that loading is lightning fast and there is plenty of content to consume on the homepage alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Hdrx2s1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kpcy1mnfcuyjvhn0qtjg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Hdrx2s1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kpcy1mnfcuyjvhn0qtjg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a href="https://francoscarpa.com/"&gt;Franco Scarpa: francoscarpa.com&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_M4OP4OG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mriv5ep10g3n19fpgjqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_M4OP4OG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/mriv5ep10g3n19fpgjqx.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Franco's site is as minimal and text-centric as it gets with a uniquely vibrant colour scheme, darkmode toggle and fully responsive design. Franco's website loads lightning fast and is &lt;strong&gt;user experience first&lt;/strong&gt; with no tracking cookies, analytics or third-party integrations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6YgWfhDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yphcgv57gssnw4rcf0sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6YgWfhDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yphcgv57gssnw4rcf0sy.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;a href="https://www.lyndseyscott.com/"&gt;Lyndsey Scott&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FVxJ8TdX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/p29b83sn090aoqbolttf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FVxJ8TdX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/p29b83sn090aoqbolttf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lyndsey is rightfully a legend around these parts and her website is nothing short of cosmic. It's a much more maximalist style with plenty of media content and fancy animations that somehow renders absolutely perfectly down to our minimal screen resolution. Not a pixel is out of place on any page even at a tiny screen size!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1AxJLezG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2yuytgikmt288axn4dtr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1AxJLezG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2yuytgikmt288axn4dtr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you know any developer websites which adapt perfectly to minimal screen sizes? Let me know down below!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Is the future of the web text-centric?</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Fri, 27 Dec 2019 18:34:51 +0000</pubDate>
      <link>https://dev.to/koabrook/is-the-future-of-the-web-text-centric-3ck9</link>
      <guid>https://dev.to/koabrook/is-the-future-of-the-web-text-centric-3ck9</guid>
      <description>&lt;p&gt;I've been thinking a lot recently about how... well... I just don't really &lt;em&gt;like&lt;/em&gt; the Internet right now. The constant bombardments of ads, rich media content like auto-playing videos and constant social media pressure causes the Internet to be an honest chore to navigate. We can all agree that especially the mobile web experience is severely obtuse right now. There's such an importance placed on responsive design and yet our modern web design philosophies seem to work directly to the contrary. &lt;/p&gt;

&lt;p&gt;Additionally, the overly data-heavy modern web is completely biased toward developed countries and high-powered devices. In countries where expensive devices and data plans are impossible to afford, Internet-enabled feature phones are barely able to keep up. Some hundred million new Internet users are making their first steps online into a connected world that is already 20 years ahead.&lt;/p&gt;

&lt;p&gt;Finally, the P2P distributed web keeps growing in both conceptual importance and usability despite bandwidth bottlenecks and other technical limitations.&lt;/p&gt;

&lt;h1&gt;
  
  
  A few questions
&lt;/h1&gt;

&lt;p&gt;Here are some theoretical questions borne from the cluttered nature of modern websites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it viable to continue to adapt modern web philosophies from 240PX-width feature phones to 1080p monitors to 4K TVs?&lt;/li&gt;
&lt;li&gt;Is it viable to create UX that adapts from mouse and keyboard to T9 to screen-readers and more?&lt;/li&gt;
&lt;li&gt;Should websites look completely different between mobile and desktop layouts?&lt;/li&gt;
&lt;li&gt;How much "in the way" is really acceptable (in-line images, PIP videos, pop-up ads)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  A path forward inspired by the past
&lt;/h1&gt;

&lt;p&gt;In my opinion the solution to many of these problems is a shift away from a media-first Internet to small, low-bandwidth, lightning fast text-first websites. In fact, the trend &lt;a href="https://francoscarpa.com/"&gt;among&lt;/a&gt; &lt;a href="https://www.taniarascia.com/"&gt;many&lt;/a&gt; &lt;a href="https://whizzoe.com/"&gt;developers&lt;/a&gt; and &lt;a href="https://dev.to/"&gt;websites&lt;/a&gt; these days is largely text-content first. Text-based websites are fast, automatically flexible, easy to develop and maintain and force you to be creative.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modern styling options mean that text can be beautiful and unique&lt;/li&gt;
&lt;li&gt;Text is infinitely scalable with no loss of quality&lt;/li&gt;
&lt;li&gt;Adapting text content to various screen sizes takes almost no work at all&lt;/li&gt;
&lt;li&gt;Text content loads quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is my take on the future of the web. I believe we'll see more websites, especially personal sites, moving away from huge frameworks with bright colours and flashing images and more toward simple layouts and text-centric content approaches. This will definitely change the way I develop sites, but how about you? What's your vision for the modern web going forward?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>discuss</category>
      <category>emergingmarkets</category>
    </item>
    <item>
      <title>I'm a Doer. How do I become a Knower?</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Mon, 23 Dec 2019 13:28:49 +0000</pubDate>
      <link>https://dev.to/koabrook/i-m-a-doer-how-do-i-become-a-knower-kip</link>
      <guid>https://dev.to/koabrook/i-m-a-doer-how-do-i-become-a-knower-kip</guid>
      <description>&lt;p&gt;Recently I've been lucky enough to score some interviews for coding jobs (specifically front-end web development) but I've hit the same roadblock every time.&lt;br&gt;
I'm a Doer. This means that I can have an idea for a website, app, etc. and &lt;em&gt;just do it&lt;/em&gt;- I can work out how to build a blog with Netlify CMS or make a WordPress Plugin or a fancy CSS animation. Typically this involves google and a lot of trial and error.&lt;br&gt;
The problem is that I'm not a &lt;em&gt;Knower&lt;/em&gt;. I don't typically know how things work. I know how to make them work, or at least I can tinker until they do, but ask me or god forbid, whiteboard me and my knowledge always comes up short. Way short. My first technical coding interview was a disaster where the solution ended up being a single line of code and a basic math problem. I've been asked "explain what a [struct/interface/doohicky] is and I don't know the answer.&lt;br&gt;
How does one bridge the gap between "I can do this" to "I know how to do this"? What's the best path forward for the Doers among us who need to build our Knower knowledge?&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Is a design overhaul a failure if you need to offer a toggle?</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Mon, 09 Dec 2019 18:45:50 +0000</pubDate>
      <link>https://dev.to/koabrook/is-a-design-overhaul-a-failure-if-you-need-to-offer-a-toggle-3cbh</link>
      <guid>https://dev.to/koabrook/is-a-design-overhaul-a-failure-if-you-need-to-offer-a-toggle-3cbh</guid>
      <description>&lt;p&gt;Iterative UX design is a staple of the modern web and every few years it seems like our favourite services get a substantial UI update. I've noticed that nearly every time a big UX update comes out, a toggle to switch between the old design and new is included or even demanded for by users. So, do you think a UX redesign that needs a toggle is a failure, or are users always going to want the old version back for a while after launch? Is a staged rollout or beta access the best way to slowly introduce such an update?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ux</category>
    </item>
    <item>
      <title>So you're considering taking a salary in bitcoin</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Wed, 04 Dec 2019 02:01:40 +0000</pubDate>
      <link>https://dev.to/koabrook/so-you-re-considering-taking-a-salary-in-bitcoin-1ab6</link>
      <guid>https://dev.to/koabrook/so-you-re-considering-taking-a-salary-in-bitcoin-1ab6</guid>
      <description>&lt;p&gt;Many startups, especially those in the Crypto space, are offering salaries paid in bitcoin. You might be wondering what it's like to get paid in bitcoin, how to handle cashing out into "real" money (or Fiat) and anything else you should expect. I'm here to answer some of your questions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would an employer pay me in bitcoin?
&lt;/h2&gt;

&lt;p&gt;This is an incredibly important question and it's worth looking into why the potential employer wants to pay in bitcoin. Most of the time there are logical reasons, for example if the company is ICO-funded (as in, its entire holdings or a large part of them is crypto) or they are making payments to international staff cheaper. Both of these are logical and aren't anything to worry about. There might also less legitimate reasons including tax avoidance. That said, it's highly doubtful a licensed company hiring staff would be doing anything under the table... I hope. Make sure you understand the reason why the company wishes to pay in bitcoin, just to be sure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would I accept a salary in bitcoin?
&lt;/h2&gt;

&lt;p&gt;You reasons may vary. Perhaps you're travelling and want to pay out to domestic banks all over the world. Perhaps there's not a reliable banking infrastructure available to you so P2P bitcoin-to-fiat trading is an advantage. Perhaps you have a nest-egg to live on and just want to lock your bitcoin away and await the prophesied "moon" (skyrocketing price). Maybe you just hate banks and want complete sovereignty over your money? These are all valid reasons to accept bitcoin.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the disadvantages of getting paid in bitcoin?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When you receive bitcoin into a wallet you own, it's absolutely, positively your responsibility to keep it safe and secure. You &lt;strong&gt;must&lt;/strong&gt; use a hardware wallet and privacy software like a good firewall. In fact, I would recommend using a hardware wallet and a bootable installation of Tails OS for all your bitcoin handling.&lt;/li&gt;
&lt;li&gt;To receive bitcoin, you provide your employer a public wallet address. This is kind of like an open deposit box for receiving bitcoin. If your employer deposits your bitcoin and for some reason you lose access to the wallet, your paycheck is as good as gone. Similarly, if you provide the wrong deposit address, say goodbye to your money.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price volatility&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Bitcoin's price fluctuates an incredible amount in any given day. I'm "lucky" enough to only have lost around $150 in a single paycheck simply because of the time it took between receiving my bitcoin and selling it which was around two hours. On the other hand, sometimes I gained a little extra.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk, reward and temptation&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;It is far too easy to fall into a trap of holding onto your bitcoin paycheck in hopes of an advantageous market instead of just paying out to fiat right away. This is how you lose money.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scammers&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Here's a cold, hard truth from my heart. In the last year I was scammed out of two pay checks equalling around $4000 on LocalBitcoins. One used a Paypal scam, the other a reversed bank deposit. I may never get this money back- bitcoin is too infant for most lawyers to care and too taboo for banks to go near. Without the proper education from your employer and preferably a payroll service, you might find yourself in similar situations. To say that my life and financial situation has changed drastically because of this would be a massive understatement.&lt;/li&gt;
&lt;li&gt;There's another side to this as well- even reputably exchange platforms (such as the infamous Cryptopia) are getting hacked and shutting down left and right. The realistic likelihood of getting any of your money back after a hack or scam is absolutely, positively 0 and if you do, well, you might want to start believing in miracles because you just experienced one!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banks and financial insitutions will hate you&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Never in my life have I had my bank account frozen, or been banned from a payment platform until I started using bitcoin. I've had two bank accounts frozen, my Paypal completely banned &lt;em&gt;forever&lt;/em&gt; and Transferwise issued me a stern warning (immediate, permanent ban) when I simply asked if I could receive a deposit from a licensed bitcoin exchange. Now they freeze my account and investigate every single transaction I make on my account, even a $20 tip I sent to a friend using my TW account. Trust me- banks &lt;strong&gt;hate&lt;/strong&gt; bitcoin. Be absolutely sure your services are okay with bitcoin. I recommend calling anonymously and asking about it. I've been okay with HSBC and Monzo after numerious KYC sessions and phone calls.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Taxes&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Be prepared to file the most annoying tax assessments in the universe. You'll definitely need a tax accountant to help you with them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In my experience (and I apologise in advance for any offense caused) the crypto community is absolutely abysmal. There are a few good eggs and little corners online but overall the space is extraordinarily toxic.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to handle getting paid in bitcoin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ask your employer for educational resources- a course or guide on how bitcoin works and how to receive and pay out your salary. Read various blogs about safely holding and trading bitcoin and make sure you know exactly what you're doing before you start. Encourage your employer to use a bitcoin payroll system such as Bitwage and to provide you with a hardware wallet such as a Trezor.&lt;/li&gt;
&lt;li&gt;Pick a licensed, trustworthy exchange that pays out in Fiat if possible- Coinbase Pro and Binance are two such platforms. Wherever possible, avoid P2P trading because they are rife with scammers, from Paypal reversals to literally getting beat up on the street during face-to-face trades. Ideally you want to stick with the same platform (or if you &lt;em&gt;have&lt;/em&gt; to use P2P, a credible, consistent partner for all your trades). Do a few small test withdrawals to make sure you understand the receive &amp;gt; convert &amp;gt; payout flow of your new paycheck.&lt;/li&gt;
&lt;li&gt;Ensure you have records of your paychecks, like invoices, so that you know what you're getting from your employer and can track your income. Remember- there're no banks or paystubs here to help you so you need to keep track of everything yourself!&lt;/li&gt;
&lt;li&gt;Before you provide a wallet address be sure to double, triple and quadruple check that you have access to the wallet in question, the wallet is secure and the deposit address is completely correct, digit-for-digit. Always copy-paste the public wallet address and still read it over several times to ensure it's correct.&lt;/li&gt;
&lt;li&gt;Check bitcoin's going rate at the time your paycheck was sent against your expected amount. As an example, let's say your salary is $3000 a month and you get paid 2.7 bitcoin for your November paycheck. For simplicity's sake let's say one bitcoin was worth $1000 the moment you got paid. Your salary is $3000 a month so you would expect to get exactly 3 bitcoin, so why did you get paid 2.7? It's very important to keep an eye on this and point out any discrepencies to your employer, just like you would with a normal paycheck. The amounts can be trickier to work out and issues like this might fly under the radar.&lt;/li&gt;
&lt;li&gt;Keep your own records of every single transaction ID that flow through your wallets for accounting purposes&lt;/li&gt;
&lt;li&gt;Have a tax accountant who knows bitcoin&lt;/li&gt;
&lt;li&gt;Have a fair amount of savings for a disaster scenario. I'd say three or four paychecks worth in cash and a few different banks just in case one gets frozen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should I take a job that pays in bitcoin?
&lt;/h2&gt;

&lt;p&gt;If you think the offer you've received is worth the trouble and you want to work in the futuristic crypto space with cutting-edge fintech platforms, then give it a try! If you know how to handle bitcoin with confidence, you shouldn't have any issues.&lt;br&gt;
Now for my personal opinion: I will never, ever take a job that pays crypto again. Regardless of the scams that very nearly cost me my life, getting paid in bitcoin simply amounted to colossal pain in the butt. Price fluctuations, long transfer times, bank freezes and endless ID verification turned my paydays into a time of anxiety, worry and stress instead of a joyful occassion to order many pizzas. Whereas I find bitcoin to be very interesting technology in concept, the availability of support and services just isn't mature enough and employers don't seem to be doing enough to facilitate end-to-end crypto payroll. My job itself was great and I enjoyed it very much, but I would not take it again. Perhaps if the pay was absurd, like six figures, I might consider it but for now it's good ol' "trustworthy" numbers-representing-unbacked-currency for me.&lt;/p&gt;

</description>
      <category>career</category>
      <category>crypto</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Elevasion: Minimising scope and literally changing the game</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Wed, 27 Nov 2019 23:48:50 +0000</pubDate>
      <link>https://dev.to/koabrook/elevasion-minimising-scope-and-literally-changing-the-game-ap9</link>
      <guid>https://dev.to/koabrook/elevasion-minimising-scope-and-literally-changing-the-game-ap9</guid>
      <description>&lt;p&gt;A while ago I released a game named &lt;a href="https://playkiseki.itch.io/elevasion"&gt;Elevasion&lt;/a&gt;. It was created in Yoyo Games' Game Maker Studio (exclusively GML) over a week or so. The game has a ZX Spectrum-esque visual style and twitchy, rapid gameplay.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AF60yTuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0l2x7s5342mxc7iei6rb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AF60yTuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0l2x7s5342mxc7iei6rb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Platformer engine
&lt;/h2&gt;

&lt;p&gt;Elevasion started life as a platformer. I had been playing the wonderful Ristar for SEGA Mega Drive at the time and was inspired by the fourth level, Planet Sonata. As you progress through the level, the denizens of the world join in with the music, forming a fantastically compressed choir. A similar mechanic with additive singing can be found in the equally amazing LocoRoco for PSP.&lt;/p&gt;

&lt;p&gt;Anyway, my idea was to follow Ristar's lead and make a slower platformer in which defeating enemies would add voices to a digital choir in the game's background music. I built a simple pixel-platforming engine in GML with the basics- running, jumping, wall-jumps and platforms and felt good about my progress. I encountered my first problem early on though...&lt;/p&gt;

&lt;h2&gt;
  
  
  I don't do music
&lt;/h2&gt;

&lt;p&gt;No really, &lt;a href="https://soundcloud.com/playkiseki/sets/future-rally-ost"&gt;I tried it before&lt;/a&gt;. Once again, I cracked out the trusty Bosca Soleil and tried to put together some basic melodies but unfortunately my tone-deaf ears only created what my partner and I call "Shem Music". Obviously with my lack of musical prowess and no budget for a musician, I couldn't achieve my game's basic concept. I had to readjust my idea into just a simple Mega Drive-style platformer instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level design
&lt;/h2&gt;

&lt;p&gt;My second challenge was before me- I had a platforming engine, but now I had to come up with appealing level designs to match it. At this point my game still had what we in the industry call "Shem Art" which made the slower platforming I had implemented rather boring and dull compared to a bright, frenetic game like Ristar. It was difficult to gauge how the game would feel without any basic graphics to work with. As I tried to work on some levels, I increased the speed of everything to make up for the lack of visual excitement. This changed the overall feel of the game once again. By this point, just a few days of deleted content later, I was already feeling overwhelmed by the incredible scope of having to create so many levels, mechanics and gimmicks, let alone graphics and music. I had never intended for it to be a long project and I knew I had to either change the scope or kill the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimising
&lt;/h2&gt;

&lt;p&gt;I really liked the engine I had built. It was simple but effective and increasing the speed made it feel quite chirpy. The jumping especially had a heavy feeling that I really liked. I decided I would minimise the project as much as possible and the cuts began:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The graphics were reduced to tiny sizes and a very limited colour pallette (inspired by the amazing &lt;a href="http://tikipod.com/rockboshersdx/"&gt;Rock Boshers&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The mechanics were reduced to simple running, jumping and wall sliding&lt;/li&gt;
&lt;li&gt;To facilitate the wall-sliding mechanic, I set the game within a very narrow playing field. A fast-moving elevator set the speed for the fast-paced gameplay perfectly&lt;/li&gt;
&lt;li&gt;In place of enemies (and potentially AI) I added falling debris for the player to avoid
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bjOHxpog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zod0affrwbotowjgrw00.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding depth
&lt;/h2&gt;

&lt;p&gt;Now that I had a basic game involving avoiding falling debris, I wanted to add some kind of mechanic to add some depth. Since there was almost no need for the wall-sliding and jumping in the gameplay yet, I built a simple multiplier which was earned by jumping over debris. More points were gained for jumping over several objects at once. Suddenly the player was encouraged to move up the wall and jump over falling obstacles. A perfect and simple risk vs. reward gimmick! In addition, I had the player colour change randomly every time a new game was started. Just a small detail to make every playthrough a little different. The pink robot is definitely the canonical colour though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Elevasion turned out to be a completely different and much smaller game than it was at its inception, which is often the case, especially for starting-out game devs. It's very important to understand realistic scopes from your projects. &lt;strong&gt;A gutted, smaller release is always much better than an unfinished one.&lt;/strong&gt; Don't be afraid to go "too small" and build up from there and definitely don't get discouraged if you find even something "simple" to be out of scope. Once you feel a pang that there may be too much, it might be good to step back and look at the bigger picture. Just level design alone could take months.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zVdEfxaq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dpj7g2x180hs5tmk51ay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zVdEfxaq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dpj7g2x180hs5tmk51ay.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end, I actually love &lt;a href="https://playkiseki.itch.io/elevasion"&gt;how Elevasion turned out&lt;/a&gt;. It has plenty of character and is the definition of a complete package with no loose ends, game-breaking bugs &lt;em&gt;(that's a challenge)&lt;/em&gt; or any sense of missing content. Except a mobile version. Why on didn't I port this to mobile? The world will never know and neither will I.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>gamemaker</category>
      <category>design</category>
      <category>scope</category>
    </item>
    <item>
      <title>JustBreathe.xyz - Taking the anxiety out of guided deep-breathing</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Mon, 25 Nov 2019 14:06:00 +0000</pubDate>
      <link>https://dev.to/koabrook/justbreathe-xyz-taking-the-anxiety-out-of-guided-deep-breathing-47em</link>
      <guid>https://dev.to/koabrook/justbreathe-xyz-taking-the-anxiety-out-of-guided-deep-breathing-47em</guid>
      <description>&lt;p&gt;There are a myriad of deep-breathing applications and websites out there that aim to alleviate stress, anxiety and improve overall well-being. Deep breathing can be a complex topic and most solutions out there provide a large number of options, settings and instructions depending on what the users want to achieve (for example calming anger, qualming an addictive urge, getting to sleep). Even worse, some apps have ads, videos and pop-ups.&lt;br&gt;
As someone who has witnessed panic attacks, I wanted to create a deep-breathing app that removed the jargon and complication and would help a user in the midst of a panic attack to breathe calmy, slowly and deeply. Hence, JustBreathe.xyz was born.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7o722hdt4i24z24dk0k2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7o722hdt4i24z24dk0k2.jpg" alt="Simple deep breathing with JustBreathe.xyz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;JustBreathe is an ultra-minimalist web-app that works on desktop and mobile browsers. On the technical side it's made in basic HTML, CSS (including transitions) and a little JS. The code is as simple as the app itself. I chose a gentle, neutral colour-scheme and an expanding-circle visual cue. The app has three settings- shallow, medium and deep (all of which follow researched deep-breathing timing) which are activated with a simple button press. Outside of this, there are no sounds, no complicated settings and nothing to distract the user from &lt;em&gt;just breathing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By saving the JustBreathe web-app to their homescreen, a panicking user can simply hit the icon and start controlling their breath immediately. No noise, no jargon, no guided tour, just simple deep breathing. The absence of clutter is incredibly important and I hope that JustBreathe.xyz solves the problem of complicated apps being too much for a panicking individual in the midst of an attack.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>productivity</category>
    </item>
    <item>
      <title>MVP to V1: Creating my portfolio website with React and the Airtable API</title>
      <dc:creator>Koa</dc:creator>
      <pubDate>Sun, 24 Nov 2019 18:06:04 +0000</pubDate>
      <link>https://dev.to/koabrook/mvp-to-v1-creating-my-portfolio-website-with-react-and-the-airtable-api-3fac</link>
      <guid>https://dev.to/koabrook/mvp-to-v1-creating-my-portfolio-website-with-react-and-the-airtable-api-3fac</guid>
      <description>&lt;p&gt;I created a personal website while on a business trip back in July 2019. It was thrown together in a couple of days using plain HTML and CSS and a pretty okay visual design. Now that I'm in the job market again and finally looking to jump into development professionally, I wanted to remake my portfolio website with a little more pizazz.&lt;/p&gt;

&lt;p&gt;I had a few requirements for this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I would start with an MVP and build upon it&lt;/li&gt;
&lt;li&gt;It had to be made in code, not with a website or blog builder&lt;/li&gt;
&lt;li&gt;It must be modular with the ability to add new projects with as little code as possible&lt;/li&gt;
&lt;li&gt;The website itself should contain a simple list of my projects&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  MVP
&lt;/h2&gt;

&lt;p&gt;With my requirements set, I took to creating an MVP product. Since the website would be a list of my projects, the MVP was also a simple list of my projects publicly available online. I used Airtable for this. &lt;a href="https://airtable.com/shre8qIxVeziaXfOQ/tblMvHba5g75ohHHM?blocks=hide" rel="noopener noreferrer"&gt;Check out the MVP here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fswvnp5986qk00g673zs7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fswvnp5986qk00g673zs7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the great things about Airtable is how it automatically generates unique API documentation for every sheet and view in the base. This was the perfect springboard into the modular concept for the site, in which I wouldn't need any code to add new portfolio entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  React web app
&lt;/h2&gt;

&lt;p&gt;I enjoy coding in React. I find the modular nature of components to be intuitive. I used React previously for &lt;a href="//smashesque.com"&gt;Smashesque.com&lt;/a&gt; and had a good time, so I went with it again. Bootstrap is my framework of choice for throwing together pretty sites so I chose to use it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modular lists using Airtable
&lt;/h2&gt;

&lt;p&gt;With the help of &lt;a href="https://dev.to/taniarascia"&gt;Tania Rascia&lt;/a&gt;'s article on &lt;a href="https://www.taniarascia.com/using-context-api-in-react/" rel="noopener noreferrer"&gt;Using Context API in React (Hooks and Classes)&lt;/a&gt;, I used Axios and the Airtable API to grab my view of choice and all the rows, fields and content therein from my MVP Airtable. My implementation is a little messy, but it worked, so no problem!&lt;br&gt;
I started with EntryContexts.js which performs the API call and creates a context state containing the spreadsheet object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EntryContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntryContextProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;axios&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.airtable.com/v0/appeDXIgWSt9xRB6n/
                    Portfolio%20Entries?api_key=[MY_API_KEY]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                            &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;
                        &lt;span class="p"&gt;})&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EntryContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/EntryContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;EntryContextProvider&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next I created a component called EntryList.js maps the EntryContextProvider component's state data into some simple HTML elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ListEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EnEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entryData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;EnEntry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ListEntry&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I created a page called &lt;code&gt;Entries.js&lt;/code&gt; which ties the &lt;code&gt;EntryContextProvider&lt;/code&gt; and &lt;code&gt;ListEntry&lt;/code&gt; components together and displays them on the page in simple React fashion. In this case, it is displayed as a list of portfolio entries on the home page of the website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EntryContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../contexts/EntryContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ListEntry&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/EntryList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Entries&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EntryContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Consumer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
                &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ListEntry&lt;/span&gt; &lt;span class="nx"&gt;entryData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="p"&gt;}}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/EntryContext.Consumer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Entries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In App.js, I wrapped my site in the EntryContextProvider component, which ensures that every page has access to the Airtable context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EntryContextProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/EntryContextProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I had the results I wanted! A simple list of all portfolio entries that were in my Airtable spreadsheet:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcybsyolz0md43i9uq9kr.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcybsyolz0md43i9uq9kr.PNG" alt="Simple list"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Aesthetic challenges
&lt;/h2&gt;

&lt;p&gt;Many developers revel with minimal websites with lists of achievements and projects. A white colour scheme and emoji are both very popular. I enjoy being a bit contrarian and a total 90s kid, so I took inspiration from the new &lt;a href="https://megadrivemini.sega.com/" rel="noopener noreferrer"&gt;SEGA MegaDrive Mini website&lt;/a&gt; and tried to match its look. Unfortunately, there's a lot of history, imagery and the theme of a retro console that helps bring the 90s Spaceship look together. Without these things (and a lack of artistic talent at my disposal) the results were less than inspiring. I realised that a dark theme for my portfolio was somewhat uninviting and less friendly than I wanted it to be, so I ended up going with a light theme. I wanted to keep some semblence of character, so I kept a scrolling background grid and gave the primary container a "sheet of paper" look. At this point I decided to add images for each project and an emoji to identify what kind of project each is, again all contained in the spreadsheet and called with the Airtable API. I hope the emoji are intuitive to anyone viewing the portfolio but the verdict is still out on that. Once everything was styled, I was extremely happy with the outcome:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvttiq9v63fwl0p0jwmva.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvttiq9v63fwl0p0jwmva.PNG" alt="Styled list"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Final Touches
&lt;/h2&gt;

&lt;p&gt;Since my website was made from scratch, I considered it an addition to my portfolio. However, I didn't want it to be added to the list with a link to itself. Therefore I added a ❔ icon in the upper-left which triggered a popover that gives more information on the site. This article will be added onto it, too:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F01gy48dmiozz2ke2lbrw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F01gy48dmiozz2ke2lbrw.PNG" alt="Popover"&gt;&lt;/a&gt;&lt;br&gt;
Finally, there was a site-breaking bug to be squashed. An empty field in the spreadsheet caused the entire Airtable context to fail, causing a blank web-page. I added some very rudimentary validation to resolve this but I didn't over-think it too much since the airtable should never have empty fields if I'm managing it. At the very least, correct entries load as they should with a simple inline error if there are any problems with a field:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3fc7ob76vhqp02b08vxp.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3fc7ob76vhqp02b08vxp.PNG" alt="Simple inline errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's about it for my V1 portfolio website! To add new projects I just add a row to the sheet, avoiding any code at all. Let's look at my requirements from the beginning of the project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I would start with an MVP and build upon it ✔&lt;/li&gt;
&lt;li&gt;It had to be made in code, not with a website or blog builder ✔&lt;/li&gt;
&lt;li&gt;It must be modular with the ability to add new projects with as little code as possible ✔&lt;/li&gt;
&lt;li&gt;The website itself should contain a simple list of my projects ✔&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, I hit all four of my requirements! It was a great journey and an interesting project. I learned the Airtable API, the importance of validation and plenty of design quirks. I'm very happy with the end result!&lt;/p&gt;
&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I enjoy the site as it is and will most likely keep it simple for now. I may use more spreadsheets to add additional list-based sections to the site- articles, testimonials, cat photos... whatever I want to add, I can do so with very little code- Clone the &lt;code&gt;Entries&lt;/code&gt;, &lt;code&gt;EntryContextProvider&lt;/code&gt; and &lt;code&gt;ListEntry&lt;/code&gt; components, replacing the Airtable API link and making any styling changes I want to.&lt;br&gt;
Airtable is not ideal for, say, entire blog posts but I'm actually curious about whether it could be done. Imagine an entire site with an Airtable backend? It's possible and perhaps I'll dabble in that idea in the future. For now, I'm happy to mark this V1 project complete! &lt;/p&gt;
&lt;h2&gt;
  
  
  BONUS
&lt;/h2&gt;

&lt;p&gt;I just added a new field to the Airtable named "order" and a new code block. With this new snippet, I can adjust the order in which the list entries appear by adding an order value in Airtable!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sortedEntries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://shemthedev.netlify.com/" rel="noopener noreferrer"&gt;Check out the live site here&lt;/a&gt;&lt;br&gt;
&lt;a href="https://airtable.com/shre8qIxVeziaXfOQ/tblMvHba5g75ohHHM?blocks=hide" rel="noopener noreferrer"&gt;Check out the Airtable backend (MVP) here&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/ShemTheDev" rel="noopener noreferrer"&gt;Check out my Github here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm currently looking for work! Drop me an email at &lt;a href="mailto:sgyll@protonmail.com"&gt;sgyll@protonmail.com&lt;/a&gt; if you'd like to chat&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>bootstrap</category>
      <category>html</category>
    </item>
  </channel>
</rss>
