Back to Table of Contents

The Skeleton

What CoPilot thinks a cat looks like

By Jesse Pence

Introduction

By the end of this chapter, we will have something that almost looks like a real app. It only took us ten chapters! Today, we will build the essential structure of our application, and we will even start to build our theme selection feature so we can see our app’s current limitations. Let’s get started!

HTML

Let’s start with the HTML. Like I said, the header and the navbar are the only things that will be statically defined in the HTML. The rest will be dynamically generated by our JavaScript. Let’s see what this looks like:

index.html

<!-- Meta Tags, etc. -->
  <body class="dark">
    <nav>
      <ul>
        <li>
          <a href="/" class="Link">Home</a>
        </li>
        <li>
          <a href="/about" class="Link">About</a>
        </li>
        <li>
          <a href="/products" class="Link">Products</a>
        </li>
        <li>
          <a href="/cart" class="Link">Cart </a>
          <span id="cart-count"></span>
          <span id="cart-total"></span>
        </li>
        <li>
          <input aria-label="Search" type="text" placeholder="Search" />
        </li>
        <li>
          <button id="toggleTheme">Toggle Theme</button>
        </li>
      </ul>
    </nav>
    <main class="main"></main>
    <footer>
      <a href="http://jazzypants.dev"> © 2023 Jovial Penguin, LLC.</a>
    </footer>
    </body>
    </html>

So, as you can see, if anything has state, it doesn’t need to be defined in the HTML. The only initial state that we have defined is the theme being defaulted to ‘dark’. While the cart’s initial state is always empty, we will be calculating what is in it dynamically so a static value is not needed.

CSS

As we all know, HTML is not much without CSS. We’re going to be keeping it extremely simple here. We’re going to be using some hard absolute positioning for the navbar and the footer, and a scrollable flexbox column for the main content. Let’s see what this looks like:

index.html

<style>
    body {
      position: absolute; /* only need to do this because of the navbar and footer */
      overflow: hidden; /* since footer is absolute, we will need a scrollbar inside */
      width: 100%; /* take up the entire screen */
      height: 100%; /* ditto */
      margin: 0; /* no margins to avoid blank area around the edges */
      padding: 0; /* no need */
      display: flex; /* flexbox makes life easy */
      flex-direction: column; /* vertical layout */
      justify-content: center; /* center everything */
      align-items: center; /* ditto */
      text-align: center; /* ditto */
    } /* this is the container that will hold everything */

    .dark {
      background-color: #222; /* DARK theme (1) */
      color: #fff; /* white text */
    }
    .dark a {
      color: #88f; /* purple links */
    } /* theme 1 (DARK) */

    .purple {
      background-color: #88f; /* PURPLE theme (2) */
      color: #ff0; /* yellow text */
    }
    .purple a {
      color: #292; /* green links */
    } /* theme 2 (PURPLE) */

    .green {
      background-color: #0f0; /* GREEN theme (3) */
      color: #911; /* red text */
    }
    .green a {
      color: #999; /* gray links */
    } /* theme 3 (GREEN) */

    nav,
    footer {
      width: 100%;
      position: absolute;
      background-color: #222; /* keep nav and footer dark */
    } /* take up whole screen at the top and bottom */

    nav {
      top: 0;
    } /* nav on top */

    ul {
      display: flex;
      justify-content: space-evenly;
      align-items: center;
      list-style: none;
    } /* make the nav into a horizontal, evenly spaced list */

    main {
      width: 100%; /* take up the whole screen */
      overflow-y: auto; /* scrollable */
      margin-top: 3rem; /* leave room for the navbar */
      margin-bottom: 1rem; /* leave room for the footer */
    }

    img {
      aspect-ratio: 1/1; /* square-ish */
      object-fit: contain; /* scale to fit */
      width: 100%; /* take up the whole area if you can */
      max-height: 50vh; /* but not too big */
    }

    footer {
      bottom: 0; /* starting at bottom */
    }

  </style>

It’s not much, but this is not a design course. We’re just trying to get something that looks like an app. If you’re building this at home, feel free to add some more CSS to make it look better. This will be it for this tutorial however.

We start off by turning the entire body of our document into a large container that permanently takes up the entire screen. Next, we define a number of themes before absolutely positioning the navbar on the top and the footer on the bottom. Along the way, we just defined some basic flexbox properties to make sure everything is centered and spaced evenly. This is all we need for full mobile responsiveness. Try expanding and contracting the window and see what happens!

Themes

While we have three themes defined in our CSS, the default selection will be dark. So, unless our user makes a selection which will be saved to localStorage, the app will always start in dark mode. Because this one selection changes many aspects of the app, that means we will need to define it as a global variable. This is always a tricky process, especially when all of our code is in one file.

To keep ourselves organized, we will be defining all of our global variables at the top of the script tag. This will make it easier to find them later on. Also, we need a way for the user to make their choice. We already made a button in the HTML skeleton, but it doesn’t really do anything yet. So, let’s keep both of these concepts in mind as we write the first JavaScript for our app:

index.html

    <script defer>
      const toggleTheme = document.getElementById("toggleTheme")

      toggleTheme.addEventListener("click", () => {
        if (document.body.classList.contains("dark")) {
          document.body.classList.remove("dark")
          document.body.classList.add("purple")
        } else if (document.body.classList.contains("purple")) {
          document.body.classList.remove("purple")
          document.body.classList.add("green")
        } else if (document.body.classList.contains("green")) {
          document.body.classList.remove("green")
          document.body.classList.add("dark")
        }
      })
    </script>

As you can see, we’re not implementing localStorage just yet. We’re just cycling through the three themes and setting the state of the application. Between this and our lack of an internal navigation system, we’re not employing any of the features of a single page application. But, I think this is important so you can see the benefits of the APIs we will be using. Let’s put everything together and see our app in action so far.

Conclusion and Demo

If you’d like to see all the code together, you can just check out the GitHub repo. Otherwise, we’ll be doing things bit by bit from now on. Here’s the app so far:

Go ahead and try it out. You can change the theme, but everything is destroyed if you click on any of the links. Even though our server ensures that the same page is loaded every time, our theme selection is forgotten every time and there is a big ugly white flash in between.

As we learned, our state lives in that one version of the HTML file. So, clicking a link destroys the current state and builds a whole new version of the file from a different URL. Even though we haven’t tied any state to the URL, the browser still has to reload the entire page. This is where the next chapter comes into play as we build our client-side routing system.

Table of Contents Comments View Source Code Next Page!