<?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: Brian P. Hogan</title>
    <description>The latest articles on DEV Community by Brian P. Hogan (@bphogan).</description>
    <link>https://dev.to/bphogan</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%2F111958%2F2e6d2f25-d9b6-4285-b982-0a4ef6bb5b5a.jpg</url>
      <title>DEV Community: Brian P. Hogan</title>
      <link>https://dev.to/bphogan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bphogan"/>
    <language>en</language>
    <item>
      <title>Split Changes into Multiple Commits</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Sun, 20 Jul 2025 17:55:14 +0000</pubDate>
      <link>https://dev.to/bphogan/split-changes-into-multiple-commits-4pmi</link>
      <guid>https://dev.to/bphogan/split-changes-into-multiple-commits-4pmi</guid>
      <description>&lt;p&gt;When working on a project, you might make several changes before you commit. However, these changes might relate to different features or bug fixes. For example, you might change a file to fix a bug, but you might see something else in the file that bugs you, so you fix it while you're there.&lt;/p&gt;

&lt;p&gt;Committing all changes at once can lead to a messy, hard-to-understand commit history or a more complex code review process. Git lets you selectively stage parts of your changes instead of adding the whole file. Using this feature, you'll get a cleaner history with smaller units of work for peers to review, as well as a chance to deal with any out-of-scope work you still want to keep but commit elsewhere.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll explore this feature by creating a small HTML project and staging your changes interactively.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need
&lt;/h2&gt;

&lt;p&gt;Git installed on your local machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Git project
&lt;/h2&gt;

&lt;p&gt;To start, create a new directory and initialize a Git repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;interactive-commit-demo
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;interactive-commit-demo
&lt;span class="nv"&gt;$ &lt;/span&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a basic HTML file called &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Interactive Commit Demo&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to My Website&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a paragraph.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Add and commit the file to your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git add index.html
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit with basic HTML structure"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now make two distinct changes to the file; change the title and add a new paragraph to the body of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Interactive Commit Tutorial&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to My Website&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a paragraph.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is a new paragraph we'll commit separately.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You made two changes to this file, but putting them in the same commit might not be best. For example, the paragraph may be a change you can get approved quickly, but the page's title might need approval by SEO experts, and you don't want it to block the publication of the paragraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Lines Interactively with &lt;code&gt;git add -p&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Use Git's Interactive mode to add only the lines you need.&lt;/p&gt;

&lt;p&gt;Run the following command to start an interactive commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see the patch on the screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/index.html b/index.html
index 7b6e59a..0d63f4e 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/index.html
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/index.html
&lt;/span&gt;&lt;span class="p"&gt;@@ -3,10 +3,11 @@&lt;/span&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;
&lt;span class="gd"&gt;-    &amp;lt;title&amp;gt;Interactive Commit Demo&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;title&amp;gt;Interactive Commit Tutorial&amp;lt;/title&amp;gt;
&lt;/span&gt;   &amp;lt;/head&amp;gt;
   &amp;lt;body&amp;gt;
     &amp;lt;h1&amp;gt;Welcome to My Website&amp;lt;/h1&amp;gt;
     &amp;lt;p&amp;gt;This is a paragraph.&amp;lt;/p&amp;gt;
&lt;span class="gi"&gt;+    &amp;lt;p&amp;gt;This is a new paragraph we'll commit separately.&amp;lt;/p&amp;gt;
&lt;/span&gt;   &amp;lt;/body&amp;gt;
 &amp;lt;/html&amp;gt;
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;s&lt;/code&gt; to split the patch into separate pieces. The screen updates to show just the title change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;Split into 2 hunks.
@@ -3,8 +3,8 @@&lt;/span&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;
&lt;span class="gd"&gt;-    &amp;lt;title&amp;gt;Interactive Commit Demo&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    &amp;lt;title&amp;gt;Interactive Commit Tutorial&amp;lt;/title&amp;gt;
&lt;/span&gt;   &amp;lt;/head&amp;gt;
   &amp;lt;body&amp;gt;
     &amp;lt;h1&amp;gt;Welcome to My Website&amp;lt;/h1&amp;gt;
     &amp;lt;p&amp;gt;This is a paragraph.&amp;lt;/p&amp;gt;
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now press &lt;code&gt;y&lt;/code&gt; to stage the title change. The next change appears, which is the change to the paragraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -7,6 +7,7 @@&lt;/span&gt;
   &amp;lt;/head&amp;gt;
   &amp;lt;body&amp;gt;
     &amp;lt;h1&amp;gt;Welcome to My Website&amp;lt;/h1&amp;gt;
     &amp;lt;p&amp;gt;This is a paragraph.&amp;lt;/p&amp;gt;
&lt;span class="gi"&gt;+    &amp;lt;p&amp;gt;This is a new paragraph we'll commit separately.&amp;lt;/p&amp;gt;
&lt;/span&gt;   &amp;lt;/body&amp;gt;
 &amp;lt;/html&amp;gt;
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;n&lt;/code&gt; to skip this change for now. You'll return to your prompt.&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;git status&lt;/code&gt; command, and you'll see that there are staged changes as well as unstaged changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On branch main
Changes to be committed:
  (use "git restore --staged &amp;lt;file&amp;gt;..." to unstage)
    modified:   index.html

Changes not staged for commit:
  (use "git add &amp;lt;file&amp;gt;..." to update what will be committed)
  (use "git restore &amp;lt;file&amp;gt;..." to discard changes in working directory)
    modified:   index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit the title:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"update title"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see one line added and one line deleted, representing the title you changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[main 41d01ad] update title
 1 file changed, 1 insertion(+), 1 deletion(-)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, use the same process again to add the paragraph change. Execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, the paragraph change patch appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/index.html b/index.html
index e0059db..0d63f4e 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/index.html
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/index.html
&lt;/span&gt;&lt;span class="p"&gt;@@ -8,5 +8,6 @@&lt;/span&gt;
   &amp;lt;body&amp;gt;
     &amp;lt;h1&amp;gt;Welcome to My Website&amp;lt;/h1&amp;gt;
     &amp;lt;p&amp;gt;This is a paragraph.&amp;lt;/p&amp;gt;
&lt;span class="gi"&gt;+    &amp;lt;p&amp;gt;This is a new paragraph we'll commit separately.&amp;lt;/p&amp;gt;
&lt;/span&gt;   &amp;lt;/body&amp;gt;
 &amp;lt;/html&amp;gt;
(1/1) Stage this hunk [y,n,q,a,d,e,?]?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;y&lt;/code&gt; to stage the change.&lt;/p&gt;

&lt;p&gt;When you return to your prompt, commit the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"add paragraph"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify your commits with &lt;code&gt;git log&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see your initial commit along with the two additional ones you made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bb403d9 (HEAD -&amp;gt; main) add paragraph
41d01ad update title
aaa6f92 Initial commit with basic HTML structure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this approach, you were able to split the changes you made to a single file into two separate commits.&lt;/p&gt;

&lt;p&gt;Sometimes, the lines may be too close together to use the automatic split feature, but you can manually edit the patch information. Consult the &lt;a href="https://git-scm.com/docs/git-add#_interactive_mode" rel="noopener noreferrer"&gt;Git documentation on editing patches&lt;/a&gt; for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Splitting your changes into multiple commits gives you fine-grained control and lets you maintain a cleaner, more logical commit history, making your project easier to understand and manage.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>git</category>
    </item>
    <item>
      <title>Jump to Git Repository Root</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Sun, 20 Jul 2025 17:51:27 +0000</pubDate>
      <link>https://dev.to/bphogan/jump-to-git-repository-root-1dk4</link>
      <guid>https://dev.to/bphogan/jump-to-git-repository-root-1dk4</guid>
      <description>&lt;p&gt;If you're working on a project in the CLI and you've navigated to a subfolder, you might want a quick way to navigate back to the project root. You can use a combination of &lt;code&gt;pushd&lt;/code&gt; and &lt;code&gt;popd&lt;/code&gt; to jump around your shell, but there's a faster way if your project is a Git repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Git installed on your machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Custom Command
&lt;/h2&gt;

&lt;p&gt;The command &lt;code&gt;git rev-parse --show-toplevel&lt;/code&gt; will tell you the path to the top-level directory of a repository. You can feed the result of that command to the &lt;code&gt;cd&lt;/code&gt; command to jump to that folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command is way too long to remember, so create an alias for it.&lt;/p&gt;

&lt;p&gt;With the Bash shell, add an alias by adding the following line to &lt;code&gt;~/.bashrc&lt;/code&gt; on Linux or &lt;code&gt;~/.bash_profile&lt;/code&gt; on macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cdr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'cd $(git rev-parse --show-toplevel)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;code&gt;zsh&lt;/code&gt;, add the alias to &lt;code&gt;~/.zshrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you open a new terminal window or &lt;code&gt;source&lt;/code&gt; your configuration file, you can use the command &lt;code&gt;cdr&lt;/code&gt; to jump to the project root.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When working in a large monorepo with subprojects, like sample code for a book or course, this is a huge time saver.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>git</category>
    </item>
    <item>
      <title>Getting Page Titles for URLs with Ruby</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Fri, 22 Apr 2022 02:21:43 +0000</pubDate>
      <link>https://dev.to/bphogan/getting-page-titles-for-urls-with-ruby-ndn</link>
      <guid>https://dev.to/bphogan/getting-page-titles-for-urls-with-ruby-ndn</guid>
      <description>&lt;p&gt;If you have a URL and you'd like to get the title for the page, you'll need to fetch the URL, parse the source, and grab the text from the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag. If you have a large number of URLs, you'll want to automate this process.&lt;/p&gt;

&lt;p&gt;You can do this with a few lines of Ruby and the Nokogiri library. In this tutorial you'll create a small Ruby program to fetch the title for a web page. Then you'll modify the program to work with an external file of URLs. Finally, you'll make it more performant by using threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetching the Title from the URL
&lt;/h2&gt;

&lt;p&gt;The Nokogiri library lets you use CSS selector syntax to grab nodes from HTML, and the &lt;code&gt;URI&lt;/code&gt; library lets you quickly read a file from a URL like the &lt;code&gt;cURL&lt;/code&gt; command does. &lt;/p&gt;

&lt;p&gt;To do this, install the &lt;code&gt;nokogiri&lt;/code&gt; and &lt;code&gt;uri&lt;/code&gt; gems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;uri
gem &lt;span class="nb"&gt;install &lt;/span&gt;nokogiri
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a Ruby script called &lt;code&gt;get_titles.rb&lt;/code&gt; and add the following code to load the libraries, open a URL as a file, send its contents to Nokogiri, and extract the value of the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'nokogiri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open-uri'&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://google.com"&lt;/span&gt; 
&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and run the program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ruby get_titles.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result shows the page title for Google:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do this for multiple URLs, put the URLs in an array manually, or get them from a file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading URLs from a File
&lt;/h2&gt;

&lt;p&gt;You may already have the list of URLs in a file, which may have come from a data export. Using Ruby's &lt;code&gt;File.readlines&lt;/code&gt;, you can quickly convert the file into an array.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;links.txt&lt;/code&gt; and add a couple of links. Make sure one of them is a bad URL; you'll make sure to handle errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://google.com
https://devto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file.&lt;/p&gt;

&lt;p&gt;Now return to your &lt;code&gt;get_titles.rb&lt;/code&gt; file and modify the code so it reads the file in line-by-line, and uses each line as a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# get_titles.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'nokogiri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open-uri'&lt;/span&gt;

&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'links.txt'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chop&lt;/span&gt;
  &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SocketError&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: can't connect. Bad URL?"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line from the file will have a line break at the end, which you remove with the &lt;code&gt;.chop&lt;/code&gt; method before storing the value in the &lt;code&gt;url&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;URI.open&lt;/code&gt; method will throw a &lt;code&gt;SocketError&lt;/code&gt; if it can't connect, and so you rescue that error with a sensible message.&lt;/p&gt;

&lt;p&gt;Save the file and run the program again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ruby get_titles.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time you see Google's page title for the first URL, and the error message for the second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google
https://devto: Can't connect. Bad URL?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version isn't the fastest when your list gets large.  On a file with 200 URLs, the process took 2 minutes. A lot of the time was the network latency. Each request takes some time to resolve and get the results. &lt;/p&gt;

&lt;p&gt;Let's make it faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing URLs Concurrently
&lt;/h2&gt;

&lt;p&gt;To make this process more efficient, and much faster, you'll need to use threads. And if you use threads, you'll need to think about thread pooling because if you use too many threads you'll run out of resources.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;concurrent-ruby&lt;/code&gt; gem makes this much less complex by giving you promises in Ruby, which have their own pooling mechanism.&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;concurrent-ruby&lt;/code&gt; gem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;concurrent-ruby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use it, you'll create a "job" for each line in the file. Each job is a promise which takes a block containing the code you want to execute. Then, following the loop, you collect all of the promises and call the &lt;code&gt;value&lt;/code&gt; method, which blocks until the promise is complete. The pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Create a job for each line&lt;/span&gt;
&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# do the work&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# get all the jobs, blocking until they all finish.&lt;/span&gt;
&lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;value!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the program to include the &lt;code&gt;concurrent&lt;/code&gt; library and create a promise for each URL read. Then get the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# get_titles.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'nokogiri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open-uri'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'concurrent'&lt;/span&gt;

&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'links.txt'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;future&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chop&lt;/span&gt;

    &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at_css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;SocketError&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: can't connect. Bad URL?"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this version of the program, you're printing the results to the screen. But you could return a value instead and print those.&lt;/p&gt;

&lt;p&gt;This time, a file with 200 URLs took around 3 seconds to process. That's a significant speed improvement and demonstrates why concurrent processing is important for these kinds of tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you used Ruby to get page titles from URLs, and you then optimized it using the &lt;code&gt;concurrent-ruby&lt;/code&gt; library to take advantage of threads and thread pooling. &lt;/p&gt;

&lt;p&gt;To keep exploring, read in the data from a CSV file and use the program to generate a new CSV file with the URL and the title in separate columns.&lt;/p&gt;

&lt;p&gt;Then see if you can pull additional information out of the URLs, such as the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; descriptions.&lt;/p&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Getting Good</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Wed, 20 Oct 2021 17:21:00 +0000</pubDate>
      <link>https://dev.to/bphogan/getting-good-3jfi</link>
      <guid>https://dev.to/bphogan/getting-good-3jfi</guid>
      <description>&lt;p&gt;So you want to get good at something in a creative field like music, writing, or even coding? Folks might tell you that you need thousands of hours of study. But that's not necessarily true. The key to getting good at something is to do it over and over while incorporating feedback along the way. &lt;/p&gt;

&lt;p&gt;Think of the one thing you're amazing at. How long did it take you to get to your level of skill? &lt;/p&gt;

&lt;p&gt;There are no shortcuts, but there is a strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Daily Practice
&lt;/h2&gt;

&lt;p&gt;Whatever you're doing, practice it daily. 30 minutes. Every day if you can. &lt;/p&gt;

&lt;p&gt;30 min a day is 3.5 hours per week which is 182 hours a year.&lt;/p&gt;

&lt;p&gt;It adds up. So does your skill.&lt;/p&gt;

&lt;p&gt;If you want to write, write every day. Even if it's crap nobody sees. Write. Learning to draw? Draw every day. You don't have to show or even keep everything you do.&lt;/p&gt;

&lt;p&gt;Want to learn to play guitar? Play every day. Nobody has to hear you all the time. It's ok to play alone.&lt;/p&gt;

&lt;p&gt;Learning a new framework? Spend 30 minutes a day working toward that goal. You might not produce some output every day. But that's ok. You're improving your understanding and making progress. After some time, you'll get more confident and your output will improve.&lt;/p&gt;

&lt;p&gt;I have over a thousand unfinished songs that nobody will ever hear. I do some kind of musical composition every day because I want to get better at it, and daily practice is how you do it.&lt;/p&gt;

&lt;p&gt;Some folks say that they'd rather dedicate a day a week and have a long block of time. That's dangerous because something might come up that causes you to have to skip that day. A soccer game for your kids, a family outing, someone gets sick, or you just feel awful.  If you miss that day, you lost a weeks' worth of practice.  IF you do your work over the course of a week, you can miss a day and you've only lost 30 minutes. &lt;/p&gt;

&lt;p&gt;Practice. Every. Day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Feedback
&lt;/h2&gt;

&lt;p&gt;Get feedback on your progress and act on it. Learning happens through feedback AND practice. Practice alone doesn't level you up, and feedback is useless if you don't incorporate it.&lt;/p&gt;

&lt;p&gt;Writing a book? Get peer reviews along the way. Work with an editor. Have these folks check your facts as well as your writing. &lt;/p&gt;

&lt;p&gt;Composing music? Share your progress with other musicians and ask them to tell you what they like and what they might suggest to make it better.&lt;/p&gt;

&lt;p&gt;Learning a framework? Use your network to find people willing to give you feedback on your progress. Share your code with them and ask them for suggestions.&lt;/p&gt;

&lt;p&gt;Get as many perspectives as you can on your work. If you've practiced enough, this will be less painful. If you haven't been practicing, this can be harder. &lt;/p&gt;

&lt;p&gt;Then take that feedback, sit on it, digest it, and identify common themes. A trusted friend or coworker or mentor can help a lot here, helping you identify common themes. This is one of the most valuable things my editors have done with me when I write a book. I get a bunch of feedback and then together we look at places where lots of folks get stuck, or where folks disagree.&lt;/p&gt;

&lt;p&gt;Some feedback might not be helpful, or you may disagree with it, and it's up to you how you want to act. You don't have to incorporate every piece of feedback, but you should act on it, either by using it or discarding it. &lt;/p&gt;

&lt;p&gt;Feedback is a gift. If you ask for it, be respectful of it, even if it stings a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do more than you watch or read
&lt;/h2&gt;

&lt;p&gt;Watching hours of videos of playing guitar, or mixing and mastering, or buying every coding course under the sun is not helping you get leveled up. Watching or reading hours of material to understand concepts is called &lt;strong&gt;passive learning&lt;/strong&gt; and it's only part of the learning process. Passive learning, including lectures, is good for getting introduced to a topic, or for seeing how an experienced person does something. And it feels like learning.&lt;/p&gt;

&lt;p&gt;But it's not all of learning. Remember, learning happens through practice and feedback. I can watch all the React courses out there, but if I don't do the practice of writing the code, and I don't get feedback, I won't get better. The same goes for any other skill. YouTube has videos on hanging and mudding drywall. And they're quite informative. But if you've ever tried to do this, you'll find that you can't just upload that info to your brain. You'll have to screw up quite a few areas of drywall before you start getting good at it.&lt;/p&gt;

&lt;p&gt;So, watch the video and follow along. Then practice that part over and over. Move on to the next lesson when you're comfortable. Make the move from passive learning to active learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You don't need thousands of hours to learn a new skill. Dedicate time every day to the thing you want to do, get feedback on it, and be sure you DO more than you WATCH.&lt;/p&gt;

&lt;p&gt;Develop a good practice plan for yourself. Put your 30 minutes on your calendar every day. Decide which days are your "passive learning" days where you'll watch videos or read papers. Then decide which are active days. Or maybe you'll do a little of both. &lt;/p&gt;

&lt;p&gt;Finally, give yourself a break. Everything you are good at now didn't happen overnight. It happend over a lifetime with little bits at a time, where you absorbed new concepts and got practice and feedback on them. This is how we learn, and there are no shortcuts. So don't get frustrated if you don't see immediate results. You know the process now. Follow it and then, one year and 182 hours later, look back on your accomplishments.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>development</category>
    </item>
    <item>
      <title>Creating Unlisted Content in Hugo</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Wed, 12 Aug 2020 14:33:02 +0000</pubDate>
      <link>https://dev.to/bphogan/creating-unlisted-content-in-hugo-340d</link>
      <guid>https://dev.to/bphogan/creating-unlisted-content-in-hugo-340d</guid>
      <description>&lt;p&gt;You may want pages that are available via a direct link, but not displayed in list pages, related content, or feeds. By default, Hugo collects all content into collections that themes use to build list pages.&lt;/p&gt;

&lt;p&gt;You can do this by adding a new piece of front matter to your pages, and then altering your themes to filter those pages out.  In this tutorial you'll see exactly what queries to change, and how to change them, so you can publish your own unlisted content.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Filtering Unlisted Pages From Queries&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Creating unlisted pages involves two steps: adding the front matter field, and modifying any iterations in your theme to ignore them.&lt;/p&gt;

&lt;p&gt;In this example, we'll use &lt;code&gt;unlisted: true&lt;/code&gt; in the front matter for a page. Create a new page called &lt;code&gt;unlisted.md&lt;/code&gt; using the &lt;code&gt;hugo&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hugo new unlisted.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;contents/unlisted.md&lt;/code&gt; in your editor and change the front matter to include a new unlisted: true field. And make sure that draft is false so Hugo generates the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;---&lt;/span&gt;
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: false
unlisted:true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll add this new field to any pages you wish to hide.&lt;/p&gt;

&lt;p&gt;Now locate your template that lists pages in your site.  The default one is usually located in your theme folder in &lt;code&gt;layouts/_default/list.html&lt;/code&gt;. You'll find logic that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    {{ range .Pages }}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ .RelPermalink }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    {{ end }}
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This logic iterates over all the &lt;code&gt;Pages&lt;/code&gt; in the site. It might say &lt;code&gt;RegularPages&lt;/code&gt; or &lt;code&gt;.Site.RegularPages&lt;/code&gt;, depending on who created the theme.&lt;/p&gt;

&lt;p&gt;To filter out the unlisted pages, change the range query to use a &lt;code&gt;where&lt;/code&gt; function to filter out any pages where the &lt;code&gt;unlisted&lt;/code&gt; field is  set to &lt;code&gt;true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    {{ range (where .Pages ".Params.unlisted" "!=" "true") }}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ .RelPermalink }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    {{ end }}
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have more specific templates, you'll need to change the scopes there as well. For example, a template that iterates through &lt;code&gt;posts&lt;/code&gt; might have a &lt;code&gt;range&lt;/code&gt; query that looks for a &lt;code&gt;type&lt;/code&gt; of &lt;code&gt;posts&lt;/code&gt;, rather than all pages on the site. Here's an example of that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ range (where .Site.RegularPages "Type" "in" "posts").ByDate.Reverse }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hugo's query language doesn't support &lt;code&gt;AND&lt;/code&gt; operations, so you'll have to nest the &lt;code&gt;where&lt;/code&gt; functions to ensure that both conditions apply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ range (where (where .Site.RegularPages "Type" "posts") ".Params.unlisted" "!=" "true").ByDate.Reverse }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some themes display the most recent blog posts on the site's home page. In your site's &lt;code&gt;index.html&lt;/code&gt; template, you might have a snippet that pulls in the most recent posts, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ range first 3 (where .Site.RegularPages "Type" "in" "posts").ByDate.Reverse }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another place you'll want to add the nested &lt;code&gt;where&lt;/code&gt; function to filter out the unlisted content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ range first 3 (where (where .Site.RegularPages "Type" "posts") ".Params.unlisted" "!=" "true").ByDate.Reverse }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll want to carry these changes through to any other specific list templates in your theme, including ones for tag lists and category lists.&lt;/p&gt;

&lt;p&gt;Once you've adjusted your general range queries, you'll want to address pagination and related content.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Removing Unlisted Pages from Pagination and Related Content&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're paginating your content, you'll use the same approach you've used before. A paginator for &lt;code&gt;posts&lt;/code&gt; in your current theme may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ $paginator := .Paginate (where .Pages "Type" "posts") }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;unlisted&lt;/code&gt; filter with a nested &lt;code&gt;where&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ $paginator := .Paginate (where (where .Pages "Type" "posts") ".Params.unlisted" "!=" "true") }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, Hugo has a built-in feature for displaying related content on your pages. You may have a partial that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ $related := .Site.RegularPages.Related . | first 5 }}
{{ with $related }}
&lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;You might also be interested in...&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    {{ range .ByDate.Reverse }}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ .RelPermalink }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; (Published {{ .PublishDate.Format "January 2, 2006" }})&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
     {{ end }}
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
{{ end }}

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

&lt;/div&gt;



&lt;p&gt;To ensure that the &lt;code&gt;unlisted&lt;/code&gt; content is filtered out, wrap the &lt;code&gt;.Site.RegularPages.Related&lt;/code&gt;function with a where clause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{ $related := (where (.Site.RegularPages.Related . ) ".Params.unlisted" "!=" "true") | first 5 }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the related content excludes the unlisted content. But you might be exposing that content in your feeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Fixing Your RSS Feed&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You've excluded your unlisted content from your pages, but don't forget about your site's RSS feed. Hugo has a &lt;a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml" rel="noopener noreferrer"&gt;default RSS template&lt;/a&gt; that you can override, but if you don't, Hugo uses this built-in default. That means you may accidentally expose your unlisted content through your feeds.&lt;/p&gt;

&lt;p&gt;To override this file, create the file&lt;code&gt;layouts/_default/rss.xml&lt;/code&gt; in your site or in your theme and modify it to exclude the unlisted pages just like you've done with other queries. &lt;/p&gt;

&lt;p&gt;The default template creates a &lt;code&gt;$pages&lt;/code&gt; variable and populates it based on various conditions, such as whether this is the home page, or if the content should be limited to a certain number of records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quickest way to hide the unlisted pages is to apply the &lt;code&gt;where&lt;/code&gt; clause after the &lt;code&gt;limit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- $pages = (where $pages ".Params.unlisted" "!=" "true") -}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be sure to use the &lt;code&gt;{{- -}}&lt;/code&gt; notation here to suppress space before and after the line.&lt;/p&gt;

&lt;p&gt;The entire RSS feed template with the changes looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- $pages = (where $pages ".Params.unlisted" "!=" "true") -}}
{{- printf "&lt;span class="cp"&gt;&amp;lt;?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?&amp;gt;&lt;/span&gt;" | safeHTML }}
&lt;span class="nt"&gt;&amp;lt;rss&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"2.0"&lt;/span&gt; &lt;span class="na"&gt;xmlns:atom=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2005/Atom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;channel&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class="nt"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;generator&amp;gt;&lt;/span&gt;Hugo -- gohugo.io&lt;span class="nt"&gt;&amp;lt;/generator&amp;gt;&lt;/span&gt;{{ with .Site.LanguageCode }}
    &lt;span class="nt"&gt;&amp;lt;language&amp;gt;&lt;/span&gt;{{.}}&lt;span class="nt"&gt;&amp;lt;/language&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Author.email }}
    &lt;span class="nt"&gt;&amp;lt;managingEditor&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class="nt"&gt;&amp;lt;/managingEditor&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Author.email }}
    &lt;span class="nt"&gt;&amp;lt;webMaster&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class="nt"&gt;&amp;lt;/webMaster&amp;gt;&lt;/span&gt;{{end}}{{ with .Site.Copyright }}
    &lt;span class="nt"&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;{{.}}&lt;span class="nt"&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;{{end}}{{ if not .Date.IsZero }}
    &lt;span class="nt"&gt;&amp;lt;lastBuildDate&amp;gt;&lt;/span&gt;{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}&lt;span class="nt"&gt;&amp;lt;/lastBuildDate&amp;gt;&lt;/span&gt;{{ end }}
    {{ with .OutputFormats.Get "RSS" }}
  {{ printf "&lt;span class="nt"&gt;&amp;lt;atom:link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;%q&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;\"self\"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;%q&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;" .Permalink .MediaType | safeHTML }}
    {{ end }}
    {{ range $pages }}
    &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class="nt"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;pubDate&amp;gt;&lt;/span&gt;{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}&lt;span class="nt"&gt;&amp;lt;/pubDate&amp;gt;&lt;/span&gt;
      {{ with .Site.Author.email }}&lt;span class="nt"&gt;&amp;lt;author&amp;gt;&lt;/span&gt;{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}&lt;span class="nt"&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;{{end}}
      &lt;span class="nt"&gt;&amp;lt;guid&amp;gt;&lt;/span&gt;{{ .Permalink }}&lt;span class="nt"&gt;&amp;lt;/guid&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{ .Summary | html }}&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
    {{ end }}
  &lt;span class="nt"&gt;&amp;lt;/channel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rss&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have other alternative formats, like a JSON feed, be sure to exclude the content in a similar way. And remember to add the unlisted: true entry to your front matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can now add a new front matter field to your content and have it excluded from Hugo's generated content lists, but still have the pages published so you can provide people with direct links to the content. &lt;/p&gt;

&lt;p&gt;Remember that in order for this to work, you'll need to change any place that iterates over your content, including lists for alternative content types, such as your site's automatically-generated sitemap or XML feed. Check and double check your site to ensure your unlisted content is truly unlisted before you publish.&lt;/p&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>hugo</category>
    </item>
    <item>
      <title>Creating a Magic 8 Ball Game with HTML, Canvas, and JavaScript</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Wed, 15 Apr 2020 00:14:14 +0000</pubDate>
      <link>https://dev.to/bphogan/creating-a-magic-8-ball-game-with-html-canvas-and-javascript-5dba</link>
      <guid>https://dev.to/bphogan/creating-a-magic-8-ball-game-with-html-canvas-and-javascript-5dba</guid>
      <description>&lt;p&gt;The "Magic 8 Ball" is a toy shaped like the "8" ball in billiards, created in 1950. You ask a "yes" or "no" question, shake the ball, and look at the answer it provides, which you see through a window on one side of the ball. &lt;/p&gt;

&lt;p&gt;In my book &lt;a href="https://pragprog.com/titles/bhwb" rel="noopener noreferrer"&gt;Exercises for Programmers&lt;/a&gt;, one of the exercises is to use arrays and random numbers to create your own Magic 8 Ball game:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a Magic 8 Ball game that prompts for a question and then displays either “Yes,” “No,” “Maybe,” or “Ask again later.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the additional challenges in the exercise is to implement this as a GUI application. In this tutorial, you'll do just that by using HTML, JavaScript, and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" rel="noopener noreferrer"&gt;Canvas API&lt;/a&gt;. When you're done, you'll have a Magic 8 Ball game that shows you answers when you click the ball. The end result will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bphogan.com/2020/04/13/magic-8-ball/images/finished.fb3a378a00b2e069e5afd4232dea66a3c581561b7239e341fd5c1b07ca163fb5.png" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F04%2F13%2Fmagic-8-ball%2Fimages%2Ffinished_hud6feae96881200afe33955dff257f646_42298_300x0_resize_box_2.87d8f7f0ccb1fc89758643ca85a2c2c7ff7e10f8226255d3f64bbd18b814fd00.png" alt="The finished ball" width="800" height="400"&gt; &lt;br&gt;
  &lt;/a&gt;&lt;br&gt;The finished ball
  &lt;/p&gt;

&lt;p&gt;The Canvas API lets you create 2D raster (pixel) graphics using JavaScript. You define a &lt;code&gt;canvas&lt;/code&gt; element on the page, grab a reference to the element in JavaScript, and then use various API methods to draw lines, rectangles, and arcs to create shapes.&lt;/p&gt;

&lt;p&gt;You'll draw the ball using the Canvas API, and then use an event handler to display random text when you click the ball. You can't modify the canvas directly once you've drawn something, so each time you click, you'll redraw the entire ball with new text.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing the Ball
&lt;/h2&gt;

&lt;p&gt;The ball will consist of three elements: a black circle for the ball itself, a blue triangle to represent the area where the text appears, and the text itself. You'll draw the ball and the triangle first, and the&lt;/p&gt;

&lt;p&gt;First create a new file called &lt;code&gt;8ball.html&lt;/code&gt; and add the following code to define a basic HTML5 skeleton with a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;8 Ball&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;  
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;canvas&lt;/code&gt; element has a &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; defined, and an ID attribute so you can grab it with JavaScript, where you'll do all your drawing. &lt;/p&gt;

&lt;p&gt;Next, add a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag below the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element that grabs a reference to the canvas using &lt;code&gt;getElementById&lt;/code&gt;, and a &lt;code&gt;ctx&lt;/code&gt; variable that holds a reference to the 2D context of the canvas. This is what you'll use to draw the ball.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;  

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a &lt;code&gt;drawBall&lt;/code&gt; function to hold the logic to draw the ball on the canvas. The function accepts a string which contains the text that will appear on the ball. Add the following code to define the function and then invoke it so that the ball will eventually appear on the page:&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;function&lt;/span&gt; &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="c1"&gt;// code goes here.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Draw the empty ball&lt;/span&gt;
&lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, write the code to create the ball itself. Start by adding the following code which draws a black filled circle on the canvas:&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;function&lt;/span&gt; &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="c1"&gt;// circle&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets the fill color to &lt;code&gt;black&lt;/code&gt; and then creates a circle using the &lt;code&gt;arc&lt;/code&gt; function. The &lt;code&gt;arc&lt;/code&gt; function takes the x and y coordinates for the center of the circle, followed by the radius, the starting angle, and the ending angle in radians. So in this case, you're creating the circle at 150 over, 150 down, with a radius of 150, a starting angle of 0 (the top), and an ending angle of PI * 2. JavaScript has &lt;code&gt;Math.PI&lt;/code&gt; available out of the box.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fill&lt;/code&gt; function then fills in the circle with the color set with &lt;code&gt;fillStyle&lt;/code&gt;. The &lt;code&gt;words&lt;/code&gt; argument has a default value of an empty string so you can call &lt;code&gt;drawBall&lt;/code&gt; with no arguments so the ball won't display any words. This is how you'll initialize the game.&lt;/p&gt;

&lt;p&gt;Save the file and reload the file in your browser. You'll see a black ball:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bphogan.com/2020/04/13/magic-8-ball/images/ball.70256017c935671ddb6e6f3d7accedd573bac5a5d6308e69cba1dc2e1927cab7.png" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F04%2F13%2Fmagic-8-ball%2Fimages%2Fball_hub3be8cd108addc86ea1133400e925596_31302_300x0_resize_box_2.7644d9014553144883ad8f61234b1b16bcb37a0c3f19d0c4e673073957ed6be1.png" alt="The ball" width="800" height="400"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;The ball
  &lt;/p&gt;

&lt;p&gt;Now define the blue triangle which will contain the words. Set the color to &lt;code&gt;blue&lt;/code&gt;, move the starting point to 150 pixels over and 50 pixels down. Then draw lines from the starting point to 50 across and 200 down, and then to 250 across and 200 down. Draw the third side by making the line end at the original point of 150 across and 50 down. Then fill the space:&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;function&lt;/span&gt; &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&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="c1"&gt;// triangle&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The triangle appears inside the ball once you save the file and reload the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bphogan.com/2020/04/13/magic-8-ball/images/ball2.b2fe07a985c7fc755be6bb1cad94d5694dff7b5b2b732ec38a3301cdd67c05d2.png" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F04%2F13%2Fmagic-8-ball%2Fimages%2Fball2_hub93e84142e4d5ced515fb49976db6bc6_36506_300x0_resize_box_2.961513d20f181a0e68ac8c495f1219634348a058d5cd7d4a13ad4cc768dd1ad2.png" alt="The ball and triangle" width="800" height="400"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;The ball and triangle
  &lt;/p&gt;

&lt;p&gt;Now let's write the game logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Random Phrase
&lt;/h2&gt;

&lt;p&gt;The Magic 8 Ball game logic boils down to having a list of possible phrases and choosing one at random, which you can accomplish with a very small amount of JavaScript code.&lt;/p&gt;

&lt;p&gt;Below the &lt;code&gt;const canvas&lt;/code&gt; line in your &lt;code&gt;script&lt;/code&gt; block, add a constant called &lt;code&gt;choices&lt;/code&gt; that holds the possible choices that will appear in your 8 ball. You can add more choices if you'd like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&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;choices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Maybe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ask Again&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a &lt;code&gt;getRandomAnswer&lt;/code&gt; function that selects a random value from the array using &lt;code&gt;Math.Random&lt;/code&gt;:&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="c1"&gt;// select an answer&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRandomAnswer&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;randomIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randomIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a random number and uses it as the array index.  The &lt;code&gt;Math.Random()&lt;/code&gt; function returns a random number between 0 and 1. The index you use to select an entry in the array needs to be between &lt;code&gt;0&lt;/code&gt; and the last index of the array, so you can take the &lt;code&gt;length&lt;/code&gt; of the &lt;code&gt;choices&lt;/code&gt; array, multiply it by &lt;code&gt;Math.random()&lt;/code&gt;, and then round the answer down with &lt;code&gt;Math.floor()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now when you call &lt;code&gt;getRandomAnswer()&lt;/code&gt;, you'll get a random value from the &lt;code&gt;choices&lt;/code&gt; array. Let's hook this up to the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Displaying the Result
&lt;/h2&gt;

&lt;p&gt;When you click on the ball, you want the text to show up inside the triangle. To do this, you'll need to add code to the &lt;code&gt;drawBall&lt;/code&gt; function to display the text, and create an event listener that fetches the random answer and draws the ball.&lt;/p&gt;

&lt;p&gt;First, add the code to display the text in the triangle. Inside the &lt;code&gt;drawBall&lt;/code&gt; function, add the following code to display the text if the &lt;code&gt;words&lt;/code&gt; argument contains any text:&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;function&lt;/span&gt; &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&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="c1"&gt;// the text&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;20px sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textAlign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a centered text area placed at 150 across and 150 down, which is in the middle of your ball.&lt;/p&gt;

&lt;p&gt;Now tie it all together by adding the event listener.&lt;/p&gt;

&lt;p&gt;After the call to &lt;code&gt;drawBall()&lt;/code&gt;, add this code which listens for &lt;code&gt;click&lt;/code&gt; events and redraws the ball, passing in the value from the &lt;code&gt;getRandomAnswer()&lt;/code&gt; function:&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="c1"&gt;// The click event that redraws the ball&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&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="nx"&gt;event&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="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getRandomAnswer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your complete project should look like the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;8 Ball&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&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;choices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Maybe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ask Again&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// circle&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// triangle&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// the text&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;20px sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textAlign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&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="c1"&gt;// Draw the empty ball&lt;/span&gt;
    &lt;span class="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// select an answer&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRandomAnswer&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;randomIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;randomIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The click event that redraws the ball&lt;/span&gt;
    &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&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="nx"&gt;event&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="nf"&gt;drawBall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getRandomAnswer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you save and reload the page and click the ball, you'll see one of the phrases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bphogan.com/2020/04/13/magic-8-ball/images/finished.fb3a378a00b2e069e5afd4232dea66a3c581561b7239e341fd5c1b07ca163fb5.png" rel="noopener noreferrer"&gt;&lt;br&gt;
    &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F04%2F13%2Fmagic-8-ball%2Fimages%2Ffinished_hud6feae96881200afe33955dff257f646_42298_300x0_resize_box_2.87d8f7f0ccb1fc89758643ca85a2c2c7ff7e10f8226255d3f64bbd18b814fd00.png" alt="The finished ball" width="800" height="400"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;The finished ball
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you created a basic implementation of a Magic 8 Ball game using the Canvas API. From here you can try the following additional exercises:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instead of a solid black color, use a radial gradient for the ball. Check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient" rel="noopener noreferrer"&gt;CanvasGradient&lt;/a&gt; documentation for more.&lt;/li&gt;
&lt;li&gt;You can animate the canvas. Rework the code that displays the text so that it fades in and fades out. Remember that to animate the canvas, you need to redraw the canvas, so you'll need to do some kind of animation loop.&lt;/li&gt;
&lt;li&gt;Try implementing this same application as a command-line tool in your favorite language, or as a mobile app. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating a Layout with CSS Grid</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Fri, 21 Feb 2020 21:07:31 +0000</pubDate>
      <link>https://dev.to/bphogan/creating-a-layout-with-css-grid-405p</link>
      <guid>https://dev.to/bphogan/creating-a-layout-with-css-grid-405p</guid>
      <description>&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" rel="noopener noreferrer"&gt;CSS Grid&lt;/a&gt; lets you create layouts with pure CSS, rather than relying on a third party framework.&lt;/p&gt;

&lt;p&gt;In this tutorial you'll use CSS Grid to create a two column layout with a header and footer that looks like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bphogan.com/2020/02/18/css-grid-layout/images/layout.1d98d42d75c3d9b84294bf73ef8789fb54a0d2b7fee4856393d5518e526438a2.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F02%2F18%2Fcss-grid-layout%2Fimages%2Flayout_hu3ddfe2bf747ea91c6d58c2774beace8c_89544_700x0_resize_box_2.2f00293d3f35d66fbd1a3a37a4483c8cd5aa4d4387729e4bfe77106b8eccfd88.png" alt="The two column layout you'll create" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;The two column layout you'll create
  &lt;/p&gt;

&lt;p&gt;First, create a new HTML page with a typical HTML skeleton with a viewport for mobile devices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My layout&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;body&lt;/code&gt; tag, add a &lt;code&gt;div&lt;/code&gt; with the class &lt;code&gt;container&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This container will hold the rest of the elements. You'll use CSS to constrain the container's width too.&lt;/p&gt;

&lt;p&gt;Inside of the container, add code to define the page's header, navbar, main content region, a sidebar full of related links, and a footer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Banner&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Main Content&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. 
      Iste culpa vitae, sunt fugit porro magni, eos non 
      mollitia officia accusantium alias illum totam eaque 
      atque sapiente reprehenderit assumenda. Nemo, modi.
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sidebar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Related content&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Lorem&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Ipsum&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Dolor&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/uL&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"footer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;Copyright notice&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each element uses an appropriate HTML sectioning element and has a class that identifies the region so you can create very specific CSS rules. You could use IDs for these, but it's a common best practice to use classes for design work and reserve IDs for elements that you'll manipulate with JavaScript. &lt;/p&gt;

&lt;p&gt;On small screens, the default browser styles will ensure that the content is readable. There's actually nothing you need to do to define this layout to work with small screens.&lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;style&lt;/code&gt; element to your page in the &lt;code&gt;header&lt;/code&gt; section to hold the stylesheet. In a larger app you'd use an external stylesheet, but keeping everything in a single file makes this project something you can reference later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
...

  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;style&lt;/code&gt; tags, add a media query that looks for a minimum width of 700 pixels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700px&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll define your layout here. All screens 700 pixels and wider will use these rules, while smaller screens won't be affected. Designing for the small screen first and then adjusting things with &lt;code&gt;min-width&lt;/code&gt; media queries often results in much less CSS code for you to write and maintain.&lt;/p&gt;

&lt;p&gt;The CSS Grid specification lets you define &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Grid_Template_Areas" rel="noopener noreferrer"&gt;grid areas&lt;/a&gt; which you can then reference to position your elements. Inside the media query, add this code to define grid areas for your header, nav, main, sidebar, and footer elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="c"&gt;/* Name your sections for your grid */&lt;/span&gt;
  &lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="nc"&gt;.header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="nc"&gt;.nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
  &lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="nc"&gt;.main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="nc"&gt;.sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sidebar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="nc"&gt;.footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;grid-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can name the grid areas whatever you like, but in this tutorial you'll use the same name as the CSS classes.  The CSS selectors reference the element and the class, ensuring a more specific match.&lt;/p&gt;

&lt;p&gt;Next,, define the &lt;code&gt;container&lt;/code&gt; region with &lt;code&gt;display: grid&lt;/code&gt;, a width, height, and margin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now use &lt;code&gt;grid-template-areas&lt;/code&gt; to define how you want your layout to work by specifying your section names in the order you want them to appear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="err"&gt;...&lt;/span&gt;

    &lt;span class="c"&gt;/* Drow your grid! */&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
      &lt;span class="s1"&gt;"header header"&lt;/span&gt;
      &lt;span class="s1"&gt;"nav nav"&lt;/span&gt;
      &lt;span class="s1"&gt;"main sidebar"&lt;/span&gt;
      &lt;span class="s1"&gt;"footer footer"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;grid-template-areas&lt;/code&gt;, you repeat the name of the region if it spans multiple columns. In this case, the &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;nav&lt;/code&gt;, and &lt;code&gt;footer&lt;/code&gt; regions span two columns, while the &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;sidebar&lt;/code&gt; regions each span one.&lt;/p&gt;

&lt;p&gt;Next, define the column widths for the grid. The main content region should be twice as wide as the sidebar. Rather than use pixels to do the math, use the &lt;code&gt;fr&lt;/code&gt; unit, which stands for "fractional unit".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now define the heights of the rows. Set the header to 100 pixels high, the navigation bar to 30 pixels high, and the footer to 80 pixels high. The main region and sidebar should take up the remaining space. so use &lt;code&gt;1fr&lt;/code&gt; for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete rule looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; 

    &lt;span class="c"&gt;/* Drow your grid! */&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-areas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
      &lt;span class="s1"&gt;"header header"&lt;/span&gt;
      &lt;span class="s1"&gt;"nav nav"&lt;/span&gt;
      &lt;span class="s1"&gt;"main sidebar"&lt;/span&gt;
      &lt;span class="s1"&gt;"footer footer"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your file and test it out. You'll see your two column layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you created a two column layout using a small amount of code. Unlike many CSS layout libraries, CSS Grid doesn't require you to add additional HTML markup or classes. The code is cleaner and it's easier to maintain. &lt;/p&gt;

&lt;p&gt;You can also place grids within grids. Use the same approach you used in this article to define your inner grids. Set &lt;code&gt;display: grid&lt;/code&gt; on one of the inner elements, and define your own grid regions. You can also combine CSS Grid with Flexbox, which is a great way to &lt;a href="https://bphogan.com/2020/02/13/css-menu" rel="noopener noreferrer"&gt;style your menu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Creating a Navigation Bar with Flexbox</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Fri, 14 Feb 2020 01:09:43 +0000</pubDate>
      <link>https://dev.to/bphogan/creating-a-navigation-bar-with-flexbox-a2n</link>
      <guid>https://dev.to/bphogan/creating-a-navigation-bar-with-flexbox-a2n</guid>
      <description>&lt;p&gt;Creating a navigation bar for a site used to be challenging, especially when you want a horizontal navigation bar on large screens. You'd need to do the math to split up your elements, account for padding and margin width, and then hope nobody wanted changes later.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox" rel="noopener noreferrer"&gt;Flexbox&lt;/a&gt; widely available in browsers, creating your navigation bar involves much less code. &lt;/p&gt;

&lt;p&gt;In this tutorial you'll build a navigation bar with elements evenly-spaced on large screens, and vertically stacked on small screens, and you'll build it all with minimal HTML and CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the HTML
&lt;/h2&gt;

&lt;p&gt;Create a new HTML file called &lt;code&gt;nav.html&lt;/code&gt; and place the following HTML skeleton in the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Navbar Example&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This template defines the document's language, the character encoding, the viewport size, so mobile devices will zoom and scale the page appropriately, and the page's title.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; of the page, add a &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element that contains a few links:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Products&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Support&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This navigation element only contains &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags. It does not contain an unordered list or other markup like you might have seen in other menu examples. This is all the markup you need to define your menu, as Flexbox can handle all of the alignment and styling. If you need to develop a more complex menu, with sections, submenus, and other elements, you might benefit from additional structural elements, but that's not the case here. &lt;/p&gt;

&lt;p&gt;Now let's add the styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling the Menu
&lt;/h2&gt;

&lt;p&gt;Styling the menu involves two steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defining the placement of elements&lt;/li&gt;
&lt;li&gt;Defining the appearance of elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start by defining the placement. The goal for this menu is that it displays vertically on small screens and horizontally on large screens. You'll end up writing less code if you build your CSS "mobile first"; in other words, style everything for the small screen first, and then add media queries for the larger screens. &lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;nav.html&lt;/code&gt; file, add a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
...
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a larger project, you'd use an external stylesheet with a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element, but for this example, keep everything in one file so you can reference it later when you're working on your own projects.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; section of the page, add a new definition for the &lt;code&gt;nav&lt;/code&gt; element that defines it as a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#The_flex_container" rel="noopener noreferrer"&gt;flex container&lt;/a&gt;, ensures its child elements are spaced apart evenly, and that the elements are stacked vertically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;justify-content&lt;/code&gt; option ensures there's even spacing between child elements, while &lt;code&gt;flex-direction&lt;/code&gt; determines whether child elements are displayed vertically stacked (&lt;code&gt;column&lt;/code&gt;), or horizontally placed (&lt;code&gt;row&lt;/code&gt;). Stacking elements vertically by default follows the "mobile first" philosophy.&lt;/p&gt;

&lt;p&gt;Next, add a media query that targets larger displays and defines &lt;code&gt;flex-direction&lt;/code&gt; again, but this time using the &lt;code&gt;row&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This media query looks for screens with a minimum width of 768 pixels. In your own projects, use your browser's developer tools to simulate mobile devices and adjust the width and identify a width that works for you. &lt;/p&gt;

&lt;p&gt;The flex container is defined, so the next step is to style the individual elements. The flex container settings you specified in the &lt;code&gt;nav&lt;/code&gt; element's style definition apply to direct child elements only. That's one reason this code doesn't use additional markup like bulleted lists. If you did that, you'd have to make the list the flex container instead.&lt;/p&gt;

&lt;p&gt;Add the following style definition to evenly space your elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex" rel="noopener noreferrer"&gt;flex&lt;/a&gt; property defines how the element will shrink or grow. Using &lt;code&gt;flex: 1&lt;/code&gt; on all of the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements makes them all take up the same size.  If you wanted one of the elements to be twice as wide as the others, you'd specify one of those to use &lt;code&gt;flex: 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;text-align: center&lt;/code&gt; centers the text inside of each element. The &lt;code&gt;margin&lt;/code&gt; and &lt;code&gt;padding&lt;/code&gt; ensure there's even spacing inside and outside of each element.&lt;/p&gt;

&lt;p&gt;This defines the placement of the elements. Now define the appearance. Add a border around each navigation item, change the color, and remove the underline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your menu is complete. Your entire file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;My Navbar Example&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&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="nt"&gt;nav&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Products&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Support&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that it does, and save the file. Open the file in your browser and you'll see your horizontal navbar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F02%2F13%2Fcss-menu%2Fimages%2Fhorizontal_hu16901f6a8a0709e1256c5acc75933cc9_13466_700x0_resize_box_2.b295f667652817e88f08e2c03d512131f65a4204a53a629ea29a2479caf952fd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F02%2F13%2Fcss-menu%2Fimages%2Fhorizontal_hu16901f6a8a0709e1256c5acc75933cc9_13466_700x0_resize_box_2.b295f667652817e88f08e2c03d512131f65a4204a53a629ea29a2479caf952fd.png" alt="The horizontal navbar" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The horizontal navbar&lt;/p&gt;

&lt;p&gt;Shrink the browser window below 768 pixels and you'll see the vertical menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F02%2F13%2Fcss-menu%2Fimages%2Fvertical_huae646a029830c60556b80986300e7249_21941_700x0_resize_box_2.a51105444baeadbb0ac16f8b65307a8e63b8154bda60c1355c08babae03057b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F02%2F13%2Fcss-menu%2Fimages%2Fvertical_huae646a029830c60556b80986300e7249_21941_700x0_resize_box_2.a51105444baeadbb0ac16f8b65307a8e63b8154bda60c1355c08babae03057b9.png" alt="The horizontal navbar" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The horizontal navbar&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you explored the Flexbox feature of CSS. Flexbox lets you build a navigation element with much less code than you could in the past, but you can also use it for things like image galleries, user interface panes, or other elements you need to align horizontally or vertically. You could even use it for entire layouts. Explore Flexbox and see how you can fit it in to your next project.&lt;/p&gt;

&lt;p&gt;Like this post? Support my writing by &lt;a href="https://bphogan.com/books" rel="noopener noreferrer"&gt;purchasing one of my books about software development&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Improving tutorials with "The Code Sandwich"</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Wed, 05 Feb 2020 23:25:09 +0000</pubDate>
      <link>https://dev.to/bphogan/improving-tutorials-with-the-code-sandwich-3hkm</link>
      <guid>https://dev.to/bphogan/improving-tutorials-with-the-code-sandwich-3hkm</guid>
      <description>&lt;p&gt;When you're writing a tutorial that involves a lot of code, you can help your readers understand the code better by using an approach I call the "code sandwich", which looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A brief high-level description of the code.&lt;/li&gt;
&lt;li&gt;The code itself.&lt;/li&gt;
&lt;li&gt;A deeper dive into the tricky bits of the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach lets the reader know what they're doing and why they're doing it, and keeps them motivated and engaged.&lt;/p&gt;

&lt;p&gt;When I'm editing content, I often come across sentences like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a new file in your editor called &lt;code&gt;index.html&lt;/code&gt; and paste in this code:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sentence is then followed by a large chunk of code without much context at all. The reader can certainly copy and paste the code, but the author hasn't given the learner a chance to understand what they've done. Some learners will stop here and dig in to the code, trying to figure it out themselves, while others will give up entirely.&lt;/p&gt;

&lt;p&gt;Replacing this with the "code sandwich" approach provides two benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It provides the context the learner needs&lt;/li&gt;
&lt;li&gt;It slows the author down and gets them to think about what they're actually trying to teach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That second bit is important; in order to teach something, you have to be able to understand it well enough to explain it. Going through the act of explaining it will then improve your own understanding of the topic. Everyone wins.&lt;/p&gt;

&lt;p&gt;Here's an example of the "code sandwich" in action. Let's start with something you might typically see which does not follow this approach:&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Create the  &lt;code&gt;Dockerfile&lt;/code&gt; in your editor and paste in the following content:&lt;/p&gt;


&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.html /usr/share/nginx/html/index.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;




&lt;p&gt;This example provides the code, but it lacks the explanation a learner might need.  Here are questions some learners might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's &lt;code&gt;nginx:alpine&lt;/code&gt; and why are we choosing this over &lt;code&gt;nginx:latest&lt;/code&gt; or another image?&lt;/li&gt;
&lt;li&gt;Why are we copying &lt;code&gt;index.html&lt;/code&gt; into the &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; folder?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're writing technical content, you're not there to answer the learner's questions, but you can use your experience to anticipate them.&lt;/p&gt;

&lt;p&gt;So here's how I'd recommend rewriting it using the "code sandwich" to provide the motivation, context, and detail:&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Create a new text file called &lt;code&gt;Dockerfile&lt;/code&gt; in your editor. Add the following code which defines a new Docker image that bundles your &lt;code&gt;index.html&lt;/code&gt; page with the Nginx web server:&lt;/p&gt;


&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.html /usr/share/nginx/html/index.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This Dockerfile uses the &lt;code&gt;nginx:alpine&lt;/code&gt; image, based on the popular &lt;a href="http://alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux project&lt;/a&gt;. Using this image as a base results in a smaller image.  By default, Nginx serves files from the &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; directory. By copying the &lt;code&gt;index.html&lt;/code&gt; file into that directory, you don't have to modify Nginx's configuration files to point to your content.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This "code sandwich" version provides the details a reader may need to understand both what and why you're having them add the code to the project.&lt;/p&gt;

&lt;p&gt;The next time you're writing a tutorial and you're asking someone to add some code to their project, consider using this approach. Examine the code, consider why you're having the reader add it, and explain what it does before you ask them to add it. Then dive deeper into the nuances. If you follow this pattern, your readers will have fewer questions, and you'll help them be more successful. After all, that's why you're sharing your words and code in the first place!&lt;/p&gt;

</description>
      <category>writing</category>
      <category>teaching</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Make Your Writing More Approachable for Learners</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Fri, 31 Jan 2020 03:12:45 +0000</pubDate>
      <link>https://dev.to/bphogan/make-your-writing-more-approachable-for-learners-19gn</link>
      <guid>https://dev.to/bphogan/make-your-writing-more-approachable-for-learners-19gn</guid>
      <description>&lt;p&gt;If you're creating technical content in order to teach others, you might be tempted to use words like "easy", "simple", or "straightforward" because you want to put the learner at ease. Unfortunately, those words can have the opposite effect. &lt;/p&gt;

&lt;p&gt;How "easy" something is depends on the learner's frame of reference. It's relative to their existing experiences. When a learner encounters something that they hear is easy, or simple, or straightforward, and they encounter a problem, they tend to internalize it; "This is easy... why am I struggling? I'm not cut out for this!" This cancels out all the good intentions you had.&lt;/p&gt;

&lt;p&gt;When I'm editing a document, article, book, or script, I have a list of words and phrases that I try to eliminate right away. I call them "bad words". Here are some of the words that tend to be demotivational.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;basically&lt;/li&gt;
&lt;li&gt;easy&lt;/li&gt;
&lt;li&gt;just&lt;/li&gt;
&lt;li&gt;obvious&lt;/li&gt;
&lt;li&gt;obviously&lt;/li&gt;
&lt;li&gt;straightforward&lt;/li&gt;
&lt;li&gt;simple&lt;/li&gt;
&lt;li&gt;simply&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I flag these and remove them, as in most cases they're not necessary. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Simply reboot the server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remove "Simply" and the sentence loses no meaning and becomes tighter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reboot the server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes I'll ask the author to add the context that might be missing. A lot of times, authors are making assumptions about the experience the readers have. Consider this passage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using Okta is a simple way to add authentication to your app. Just add it to your project. Much easier than writing your own authentication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you look at the documentation for Okta, it's anything but simple. And you don't "just" add it to your project. You need an account. You need to create users. You need to import the appropriate libraries, and integrate the code into your codebase. &lt;/p&gt;

&lt;p&gt;If you've done this a hundred times, you may indeed find this easy. As an author, you have to remember that you're writing something like this to teach others. You want them to be successful. So you may have to share the details so the learner has the right frame of reference, and can make the call if it's "easy" for themselves. Consider this version instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Okta lets you add authentication to your app instead of writing your own logic, which will be more complicated and more prone to security vulnerabilities. In this tutorial you'll sign up for an Okta account, obtain the API keys you need, create your users, and integrate Okta's JavaScript code with your app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This version tells the reader what's in store, including an explanation as to why Okta is a good choice over writing your own implementation. &lt;/p&gt;

&lt;p&gt;Yes, it's much more text. But readers can skip it. Assume your readers are as smart as you. Give them all the pieces and let them decide what's easy, and what they can skip.&lt;/p&gt;

&lt;p&gt;I suggest adding words like "easy", "simple", and others to your text editor's spelling dictionary as misspelled words so they show up as you're writing or editing. &lt;/p&gt;

&lt;p&gt;You can also install plugins for your editor that focus on improving writing. The &lt;a href="https://github.com/TravisTheTechie/vscode-write-good" rel="noopener noreferrer"&gt;write-good&lt;/a&gt; plugin for Visual Studio Code is handy, and you can find similar plugins for &lt;a href="https://github.com/davidbeckingsale/writegood.vim" rel="noopener noreferrer"&gt;Vim&lt;/a&gt; and &lt;a href="https://github.com/bnbeckwith/writegood-mode" rel="noopener noreferrer"&gt;Emacs&lt;/a&gt;. These will find some of the words I listed, among other phrases and words that cause your writing to be weaker than it could be. Be warned though; these plugins have opinions of their own, and may ignore some words while flagging others. Tweak them to find what works best for your learners. After all, you're writing for them, not just for yourself.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>teaching</category>
    </item>
    <item>
      <title>Using the tree Command for Software Projects</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Wed, 22 Jan 2020 12:56:16 +0000</pubDate>
      <link>https://dev.to/bphogan/using-the-tree-command-for-software-projects-3ekl</link>
      <guid>https://dev.to/bphogan/using-the-tree-command-for-software-projects-3ekl</guid>
      <description>&lt;p&gt;The &lt;code&gt;tree&lt;/code&gt; command is a handy tool for visualizing directory structures. The default options recursively show all the files and directories in the current directory. &lt;/p&gt;

&lt;p&gt;In this article you'll explore some of &lt;code&gt;tree&lt;/code&gt;'s options and create an alias for the &lt;code&gt;tree&lt;/code&gt; command that you can use to visualize your software projects.&lt;/p&gt;

&lt;p&gt;To follow along, create a Node.js project with a directory structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;files
├── bin
├── src
│   └── components
└── test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the following command to build that structure quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; files/&lt;span class="o"&gt;{&lt;/span&gt;bin,src/components,test&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the &lt;code&gt;tree&lt;/code&gt; command to verify that the structure you created looks like the structure specified previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree files

files
├── bin
├── src
│   └── components
└── &lt;span class="nb"&gt;test

&lt;/span&gt;4 directories, 0 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch to the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it a Git repository, which will create a hidden &lt;code&gt;.git&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;npm&lt;/code&gt; to create a &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, install Webpack as a dependency, which will create and populate the &lt;code&gt;node_modules&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; webpack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have a project you can use to explore &lt;code&gt;tree&lt;/code&gt; in more detail. &lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;tree&lt;/code&gt; on your project now and you'll see too many files to fit on the screen due to the number of files in the &lt;code&gt;node_modules&lt;/code&gt; directories. But that's not even showing you the full story, as the &lt;code&gt;tree&lt;/code&gt; command doesn't show hidden files by default.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;tree -a&lt;/code&gt; to see everything. And since there's so much to see, pipe it to the &lt;code&gt;less&lt;/code&gt; command so you can view the results one page at a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt; | less
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press &lt;code&gt;Space&lt;/code&gt; to page through the results or press &lt;code&gt;q&lt;/code&gt; to quit and return to the prompt.&lt;/p&gt;

&lt;p&gt;There's a lot of output you probably don't need to see all the time. The &lt;code&gt;-I&lt;/code&gt; flag lets you supply a list of directories to ignore:&lt;/p&gt;

&lt;p&gt;The following command runs &lt;code&gt;tree&lt;/code&gt; but ignores the &lt;code&gt;.git&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s1"&gt;'.git`
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the pipe (&lt;code&gt;|&lt;/code&gt;) character, you can ignore multiple directories. To ignore both the &lt;code&gt;.git&lt;/code&gt; and &lt;code&gt;node_modules&lt;/code&gt; directories, execute this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s1"&gt;'.git|node_modules'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the output is more manageable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── bin
├── package-lock.json
├── package.json
├── src
│   └── components
└── test

4 directories, 2 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--dirsfirst&lt;/code&gt; option lists the directories first, then the files.  If you have a project with many files in the root, this option might be useful. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="s1"&gt;'.git|node_modules'&lt;/span&gt; &lt;span class="nt"&gt;--dirsfirst&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output now shows the &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; at the bottom of the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── bin
├── src
│   └── components
├── test
├── package-lock.json
└── package.json

4 directories, 2 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command's gotten pretty long, and it'll be even longer if you need to pipe the results to the &lt;code&gt;less&lt;/code&gt; command. To make it easier to use, use a shell alias.&lt;/p&gt;

&lt;p&gt;If you're using Bash, add an alias to your &lt;code&gt;~/.bashrc&lt;/code&gt; file on Linux or your &lt;code&gt;~/.bash_profile&lt;/code&gt; on macOS. If you're using ZSH, add the alias to the &lt;code&gt;~/.zshrc&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;devtree&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tree -aC -I '.git|node_modules|bower_components' --dirsfirst | less -FRX"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-C&lt;/code&gt; flag for the &lt;code&gt;tree&lt;/code&gt; command ensures that &lt;code&gt;tree&lt;/code&gt; displays colored output. Some operating systems colorize the output by default through its own alias. The &lt;code&gt;-F&lt;/code&gt; flag on &lt;code&gt;less&lt;/code&gt; tells &lt;code&gt;less&lt;/code&gt; to exit if there's only one page of results, while the &lt;code&gt;-R&lt;/code&gt; and &lt;code&gt;X&lt;/code&gt; flags preserve colors and other terminal characters from the &lt;code&gt;tree&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;With the alias in place, you can open a new Terminal session and use the &lt;code&gt;devtree&lt;/code&gt; command to execute the command. Navigate to the &lt;code&gt;files&lt;/code&gt; directory you specified and run the command to try it out.&lt;/p&gt;

&lt;p&gt;In this tutorial, you used several &lt;code&gt;tree&lt;/code&gt; command options to customize its output, and then combined it with the &lt;code&gt;less&lt;/code&gt; command to ensure it'll be readable. Using shell aliases,  you saved the long command so it's more usable.&lt;/p&gt;

&lt;p&gt;If you enjoyed this tutorial, check out &lt;a href="https://smallsharpsoftwaretools.com" rel="noopener noreferrer"&gt;Small Sharp Software Tools&lt;/a&gt;, my book on command-line tools.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>development</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Implementing "Who's in Space" with Vue</title>
      <dc:creator>Brian P. Hogan</dc:creator>
      <pubDate>Tue, 14 Jan 2020 13:32:55 +0000</pubDate>
      <link>https://dev.to/bphogan/implementing-who-s-in-space-with-vue-4onf</link>
      <guid>https://dev.to/bphogan/implementing-who-s-in-space-with-vue-4onf</guid>
      <description>&lt;p&gt;One of my favorite practice exercises from my book &lt;a href="https://pragprog.com/titles/bhwb" rel="noopener noreferrer"&gt;Exercises for Programmers&lt;/a&gt; is the "Who's In Space?" problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did you know you can find out exactly who’s in space right now? The Open Notify API provides that information. Visit &lt;a href="http://api.open-notify.org/astros.json" rel="noopener noreferrer"&gt;http://api.open-notify.org/astros.json&lt;/a&gt; to see not only how many people are currently in space but also their names and which spacecraft they’re on.&lt;/p&gt;

&lt;p&gt;Create a program that pulls in this data and displays the information from this API in a tabular format. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I like this program for a few reasons. First, it's a little more challenging than "Hello World". Second, it's a chance to pull data from a remote source that doesn't require authentication, so I get to practice consuming data without having to worry about getting an API key. Finally, it's just plain fun.&lt;/p&gt;

&lt;p&gt;Let's implement a solution to this in &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;.  To fetch the data, we'll use &lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt;, a promise-based library for making web requests.  &lt;/p&gt;

&lt;p&gt;This will be a single HTML page; we don't need Vue's CLI tooling for this. We'll link to the Vue and Axios libraries in the HTML page.  &lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;p&gt;First, explore the API. When you make a request to &lt;code&gt;http://api.open-notify.org/astros.json&lt;/code&gt; you'll see results that look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "people": [
    {
      "name": "Christina Koch",
      "craft": "ISS"
    },
...
  ],
  "number": 6,
  "message": "success"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API returns an object with three keys: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;people&lt;/code&gt;, containing the list of people in space&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;number&lt;/code&gt;, containing the number of people in space&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;message&lt;/code&gt;, containing a status message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our app will take the &lt;code&gt;people&lt;/code&gt; field and display it in an HTML table.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;space.html&lt;/code&gt; and add an HTML template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Who's In Space&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;div&lt;/code&gt; with the ID of &lt;code&gt;app&lt;/code&gt; is where you'll connect your Vue instance.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;div&lt;/code&gt; tags, add an HTML table for the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Craft&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the &lt;code&gt;table&lt;/code&gt;, add a link to the Vue library itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that line, add a link to Axios:&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;script&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.1/axios.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&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;Now that the libraries are loaded, add a new &lt;code&gt;script&lt;/code&gt; block and define a variable called &lt;code&gt;url&lt;/code&gt; to hold the URL to the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://api.open-notify.org/astros.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Then below the &lt;code&gt;const url&lt;/code&gt; line, add the following code to define a new Vue instance:&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;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;data &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="na"&gt;people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;created &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Fetching data here&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;el&lt;/code&gt; field connects, or "mounts" this Vue instance to the DOM element with the ID of &lt;code&gt;app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;data&lt;/code&gt; section returns an object with a field called &lt;code&gt;people&lt;/code&gt;, which is set to &lt;code&gt;null&lt;/code&gt; by default. The &lt;code&gt;created&lt;/code&gt; section is where you'll fetch the data. &lt;code&gt;created&lt;/code&gt; is one of Vue's &lt;a href="https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks" rel="noopener noreferrer"&gt;lifecycle hooks&lt;/a&gt;. It fires before the Vue instance is actually connected to the DOM.&lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;created&lt;/code&gt;, use Axios to make the request to the API and store the results in the &lt;code&gt;people&lt;/code&gt; field of your Vue instance:&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="nf"&gt;created &lt;/span&gt;&lt;span class="p"&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="nx"&gt;url&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;response&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;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;people&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Axios gets the results and stores them in &lt;code&gt;response.data&lt;/code&gt;. Remember that the API's response contains three keys: &lt;code&gt;people&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, and &lt;code&gt;message&lt;/code&gt;. Since you only need &lt;code&gt;people&lt;/code&gt;, you pull down only that part of the results using dot notation.&lt;/p&gt;

&lt;p&gt;Now that you have the data, display the results. Use Vue's &lt;code&gt;v-for&lt;/code&gt; directive to iterate over the results, creating table rows that display the person and the craft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Craft&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- add this --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"person of people"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ person.name }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ person.craft }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- add this --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The completed solution looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en-US"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Who's In Space&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Name&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Craft&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"person of people"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ person.name }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ person.craft }}&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.1/axios.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://api.open-notify.org/astros.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nf"&gt;data &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="na"&gt;people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;created &lt;/span&gt;&lt;span class="p"&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="nx"&gt;url&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;response&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;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&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;people&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you view the page in your browser, you'll see the results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F01%2Fimplementing-whos-in-space-with-vue%2Fimages%2Fresults.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbphogan.com%2F2020%2F01%2Fimplementing-whos-in-space-with-vue%2Fimages%2Fresults.png" alt="The people in space right now" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This implementation doesn't have any styling, but the functionality is there. In the &lt;a href="https://pragprog.com/titles/bhwb" rel="noopener noreferrer"&gt;full book&lt;/a&gt;, there are some additional challenges for this exercise, as well as 56 other exercises you can tackle in any language you choose. &lt;/p&gt;

&lt;p&gt;Now that you have this working in Vue, try implementing it with another language or framework.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
