Hajime, the duck guy

Monday, December 23, 2024, by Hajime Yamasaki Vukelic

About this series

Step-by-step guide for building applications using nothing but browser APIs – no build, no tools, no frameworks

  1. Introduction to vanilla, goals, principles
  2. Requirements, architecture, content structure
  3. SVG spritesheet for icons
  4. Data molding, HTTP requests, basic event handling
  5. Storage, advanced event handling
  6. MPA, teamwork and shared code, separation of concerns
  7. Webfonts, asset preloading, responsive grid layouts
  8. Responsive tables using a grid layout with subgrids
  9. Toolless lazy loading and micro-frontends (part 1)
  10. Toolless lazy loading and micro-frontends (part 2)
  11. Reverting back to classic MPA and comparison with micro-frontend
  12. Code cleanup and vanilla CSS-only view transitions
  13. Accessible tabbed interface and a simple feature flag
  14. Visualization using vanilla SVG (part 1)
  15. Visualization using vanilla SVG (part 2)
  16. Visualization using vanilla SVG (part 3)
  17. PWA and offline mode

MPA, teamwork and shared code, separation of concerns

In the previous part, I finished off the place selection page. In this part, we will start working on the actual weather information page.

I'm going to start this page fresh as if the other page does not exist. This is a simulation of team effort where two members (or two groups) would be working on two separate contexts. (If you are not sure what 'context' means. I recommend you start from the beginning of this series.) It will also show if I got the separation between the contexts right. If I can get all or at least most of the page done without having to worry about what the other page is doing, then it's a successful separation.

The page header

The first thing I'm doing is copying the heading from the other page. That's a common part that is shared between the two pages.

<body>
    <header>
        <h1>
            <x-icon name="logo"></x-icon>
            A weather app
        </h1>
    </header>
</body>

Some people will frown at copying the markup as is. It's breaking the single source of truth principle, not DRY, etc. Yes, that's absolutely correct. However, here's why I believe this is a pragmatic choice.

Let's suppose that I'm going to honor the SSoT principle. First let's discuss with which parts of the markup I should be reusing. What are the "truths" in the structure? The truths here are the name of the icon and the title of the site. That's the information that should be the same everywhere. The truth is also the relationship between these two pieces of information: that they are part of the level 1 heading. The truth is also that they appear inside the page banner, at the top. If I were to establish a single source of truth for each and every truth, then I would need to create a template that includes these things, and render it in the HTML somehow.

I could facilitate reuse either client-side or using a build script. Client-side, I'd put the content in a template, render it using a custom element, so the whole thing would become just <x-page-header> or something. The other approach is to use a template engine like EJS, Nunjucks, or just template literals with NodeJS, and render the page header that way.

Now let's suppose I did that. My next step is to add the name of the place and a button the change the place in the header. Oops. That's not in my template. Premature reuse. I'd now be faced with some choices:

  • Do I scrap the templates, and duplicate the whole markup across pages? (So back to what I've just done.)
  • Do I duplicate the <header> tag only and keep the logo as a reusable component?
  • Do I modify the template so that I can insert arbitrary content after the logo?
  • Do I modify the template so that it also includes the place label and button that is turned on using some configuration option?

These choices and the need to decide have already wasted my time, not to mention that I would need additional work to implement whatever I decide to do. In hindsight, just copying the code would start to seem like a good idea.

Of course, there are still people saying it's "worth it", because you're gonna want to change the title or the logo at some point. What if you decide you need a menu to the left of the logo later? Or, you want to show the summary of the weather in the logo? Etc. Ok, fine. Let's explore that. First of all, for each "what if", you can say "what if not". Unless we are reasonably certain it's coming (because we've worked on many apps like this and we just know someone's gonna ask for it at some point), it's cheaper to just not do it now, speaking from experience (YMMV). Let's just stick to what we already have, and talk about how likely that's going to change.

  • Logo
  • Title of the app
  • Relationship between them

How bad would it be if I'd need to change these things? Since we're talking about reuse, we are going to assume that any change is going to be reflected in all places where these occur – if I change the title, it has to be changed everywhere the same way. The tl;dr is that it's going to be relatively painless whatever I do. Why? Because I only have two pages. Might have have three pages or more in future? Sure, I might. Might even have more. But then I might not. Who knows. So for now, I only have two pages, and changing this in one place, and copying to another is not the biggest deal in the world. For two reasons: it's not a lot of markup to begin with, and it's easy to locate – there isn't a lot of very similar markup, or a lot of text that is identical.

If you want an example of something actually worth reusing, I've already done so with icons. I incurred the penalty of client-side rendering and tighter coupling, so that I can reuse the icon markup everywhere. Why? Because of the number of icons I'm "reasonably sure" I'll be using and the pain of locating and modifying the markup across different usages that I'm very familiar with. 🥲

You get the picture. It's a decision. It's not a rule that says "always copy" or "never copy". Such rules are rare in programming, so the next best thing you can get is a collection of heuristics that you can quickly recall based on experience: if I detect (insert pattern) then I normally (insert action). The whole discussion about the page headers has happened enough time in my career that it's, over time, become distilled into "Ah, header: copy it!" YMMV, and you'll find your own heuristics.

Speaking of the header, I'm adding the current place placeholder and a button to change it, and I'll wrap that in a custom element for easier access in CSS and JavaScript.


<header>
    <!-- .... -->

    <x-current-place>
        <p>
            <span class="alt-text">Showing weather for</span>
            <span class="place-name"></span>
        </p>
        <a href="place.html">
            <x-icon name="edit"></x-icon>
            <span class="alt-text">Change</span>
        </a>
    </x-current-place>
</header>

Let me break it down. The outer custom element, <x-current-place> holds two pieces of content: the label that is formulated as a sentence "Showing weather for (place name)", and a link to the place.html page, with an icon and the label "Change".

The "Showing weather for" and "Change" label are hidden via the .alt-text class I introduced in part 2. They are only provided for screen reader users for clarity.

As usual, I provide a <span> as a placeholder for text with a class so I can get to it easily.

Outside-in

Sometimes I work out content structure inside-out. For instance, if I'm doing a form, like the one on the other page, I'm going to fist think about what fields I need, and then work myself out towards the outer perimeter of the page. But in this case, it's a bit different. I'm going to do it outside-in, because the actual contents of the page are a bit vague at this point. I want to first capture the major sections first, and then fill them in one by one.

With this approach I have the choice of first completing all markup on the page, or developing the page completely section by section. For example, I could complete the weather summary first, including its CSS and JS, and then move on the weather forecast, or first do the markup for both sections, and then the CSS, and then the JS. In this particular app, it probably doesn't matter which approach I take, but for larger apps, doing it one way or another could make a big difference.

In fact, doing it section by section in full is also a good approach for process reasons. Rather than detailing the entire page all at once, I can simply ask the stakeholders for feedback on a particular well-defined section before I even start working on the other ones. This speeds up the work considerable, in my experience. I could also have another team member own another section and work on it in independently, provided we work out a good protocol for communicating between them.

In this particular case, though, I'm going to do the whole page in one go.

For the purposes of the weather information page, I have two sections which I'm marking up first:


<body>
    <!-- .... -->
    <main>
        <x-weather-summary></x-weather-summary>
        <x-weather-forecast></x-weather-forecast>
    </main>
</body>

In larger projects, I could also put the hidden attribute on these sections. That could serve as the "poor man's feature flag". The elements would be hidden from the end-user even though the markup is there. If I release the page without the JavaScript for the custom elements, the markup would remain hidden. Yes, a bit of extra code shipped, but I can safely ship with half-finished code, without having to worry about removing it first.

Weather summary content structure

I move on the the weather summary section. This section will include a summary of the current weather conditions. This includes weather type (sunny, cloudy, etc.), temperature (current, min, max), atmospheric pressure, humidity, precipitation probability, wind direction and speed.


<x-weather-summary>
    <section>
        <h2 class="alt-text">Current weather</h2>

        <dl>
            <div class="weather">
                <dt class="alt-text">Weather</dt>
                <dd></dd>
            </div>
            <div class="current-temperature">
                <dt class="alt-text">Current temperature</dt>
                <dd></dd>
            </div>
            <div class="min-temperature">
                <dt>Minimum temperature</dt>
                <dd></dd>
            </div>
            <div class="max-temperature">
                <dt>Maximum temperature</dt>
                <dd></dd>
            </div>
            <div class="precipitation-probability">
                <dt>Precipitation probability</dt>
                <dd></dd>
            </div>
            <div class="humidity">
                <dt>Humidity</dt>
                <dd></dd>
            </div>
            <div class="wind">
                <dt>Wind</dt>
                <dd>
                    <span class="wind-speed"></span>
                    <x-icon class="wind-direction"></x-icon>
                </dd>
            </div>
        </dl>
    </section>
</x-weather-summary>

The <h2> header makes the section a subsection of the page (which is <h1>). I'm treating this heading as a text alternative because the design is going to make it evident that this is the current weather summary.

The weather summary details are coded as a definition list. The definition list are used to associate labels or terms with their descriptions or details. While they are commonly used for term definitions or translations, I can also use them for this kind of scenario where the "term" is the label, and the "definition" is the details for that label. The official spec calls them 'name-value pairs'. I've hidden the labels for the weather and current temperature as I think they don't need additional labels. Screen readers will usually read them as "list".

For styling purposes, I can wrap the <dt>-<dd> pair in a <div>. If you do this, be careful: you can only wrap it in a single <div>. You can't nest multiple <div>s. The wrapper <div> is for layout purposes later. I want to have something to grab onto when I'm laying out the grid. Ditto with the class names on the div.

Weather forecast content structure

I'm laying out the the 10-day forecast. It should include the following items:

  • Date (Date, month, day of week)
  • Weather type (sunny, cloudy, etc.)
  • Minimum / maximum temperature (same column)

Additionally, it would be useful to include the following:

  • Precipitation probability
  • Humidity
  • Maximum wind speed

This type of data can be presented either as a table or a list. Although there are presentational considerations for picking one or the other, I'm currently primarily concerned with the semantics. Tables are matrices for relating various pieces of information along two axes. Lists, on the other hand, do not have this property. List items can group data together, but individual pieces of data in each list item isn't directly related.

What do I mean by this? In a table, you can go from the tomorrow's temperature to the temperature on the day after. Visually this is done by moving down one row while focusing on the temperature column. With lists, you basically skip the the next item, and then you drill down to find the temperature, so it's a two step process. While we can visually presents things either way, we must make sure they are semantically correct first because not all users rely on the visual presentation.

The most common usage of the 10-day forecast (as opposed to a forecast for specific dates) is to scan for trends. Most commonly, people scan for temperature and the weather type (e.g., is it going to be sunny next week), and occasionally other information such as precipitation probability or wind. For simplicity, I'm not including the precipitation and wind in the 10-day forecast.

Tables are usually the best way to present information that needs to be scanned, and they also work well enough for looking up specific rows, so I'm going with tables.

Semantics are important, but, visual presentation cannot be entirely disregarded. Due to the way HTML and CSS interact with each other, it's not always possible to get an ideally clean separation of the structure and presentation. (Philosophically, we could also argue that presentation and structure are inseparable because graphical presentation conveys the structure, and that's also correct.) For instance, on small screens, the full 6-column table would be a bit cumbersome, so there's a need to structure the information differently, like providing the last 3 columns in a pop-up dialog that is brought up on demand.

This affects how I'm going to deal with the markup. As you know, it's impossible to add additional markup in CSS, so I cannot just add a button and a pop-up dialog to a 6-column table. I have to put these things in the markup right from the start. Semantically I'm expressing a structure that allows the user to reveal additional details on demand. The <details> element would be the best fit for what I intend. However, for practical reasons, I'm opting for a <dialog> element. Simply using the proper <dialog> instead of replicating its behavior using CSS and JavaScript while using the <details> element is much less code. The trade-off is that I lose the semantic relationship between the trigger and the pop-up inherent in the <details> element, but it's not a huge loss – at least it doesn't seem like it would be at this point – as the trigger button label would provide a hint for the intended behavior.

<x-weather-forecast>
    <h2 class="alt-text">10-day forecast</h2>

    <table>
        <thead>
            <tr>
                <th scope="column">Date</th>
                <th scope="column">Weather</th>
                <th scope="column">Temperature</th>
                <th scope="column">Additional information</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
</x-weather-forecast>

The body is empty. It will be populated from a template. The template represents the single row in the table. Note the scope attribute on the header cells. You should generally always include it. It helps screen readers correctly associate headers with data cells.

And now for the individual row template:

<x-weather-forecast>
    <!-- .... -->

    <template>
        <tr>
            <th scope="row" class="date"></th>
            <td class="weather">
                <x-icon class="icon"></x-icon>
                <span class="description"></span>
            </td>
            <td class="temperature">
                <x-icon name="temp-min"></x-icon>
                <span class="alt-text">Minimum temperature</span>
                <span class="min-temperature"></span>
                /
                <span class="alt-text">Maximum temperature</span>
                <x-icon name="temp-max"></x-icon>
                <span class="max-temperature"></span>
            </td>
            <td class="additional-information">
                <button>
                    <x-icon name="info"></x-icon>
                    <span class="alt-text">More information</span>
                </button>

                <dialog>
                    <div class="dialog-content">
                        <div class="dialog-header">
                            <h3 class="alt-text">Additional weather information</h3>
                            <button>
                                <x-icon name="x"></x-icon>
                                <span class="alt-text">Close</span>
                            </button>
                        </div>

                        <dl>
                            <div class="precipitation-probability">
                                <dt>Precipitation probability</dt>
                                <dd></dd>
                            </div>
                            <div class="humidity">
                                <dt>Humidity</dt>
                                <dd></dd>
                            </div>
                            <div class="wind">
                                <dt>Wind</dt>
                                <dd>
                                    <span class="wind-speed"></span>
                                    <x-icon class="wind-direction"></x-icon>
                                </dd>
                            </div>
                        </dl>
                    </div>
                </dialog>
            </td>
        </tr>
    </template>
</x-weather-forecast>

Although there's a lot of markup here, there's actually nothing special going on. There's an icon for the weather that has no name attribute, and I'm going to be populating that from the data. As usual, the empty <span> are placeholders for data. The definition list was yanked from the weather summary.

Inside the dialog, I use a <div class="dialog-content"> for use in backdrop click detection. There's no genius through process behind this. It's just something I always end up doing, so I just do it. The dialog is also garnished with a button for closing it.

The button does not have any ARIA attributes. For instance, I didn't use aria-haspopup. In my opinion, the label of the button, "More information", combined with the fact that it's a button, already suggests that "more information" would be revealed somehow when the button is activated. Whether the particular method of revealing this information is a popup isn't that relevant, as the user would be notified that they're in a dialog as soon as they activate the button. (The visually able users will see the traditional info button, "i" in a circle, instead of the label. This generally suggests a popup or a toggle tip.) I think with careful wording and with the help of the context, we can generally forego a lot of the ARIA stuff.

I'm stressing again that this is my opinion, not a directly officially endorsed principle. I'm not saying that you should never use ARIA attributes but I'm lately leaning towards not using them at all when possible, and focusing more on text alternatives and visible text labels.

Conclusion

As far as the content structure goes, I think I'm now in an ok place. There are some parts that I'm not 100% sure how they would play out, and I've got some ideas about other things I could do with the page (e.g., I intend to also include some visualization) that I haven't addressed in the markup. But anyway, one step at a time.

In the next part, I'll be working on the styling.

Posted in Programming tips
Back to top