Examples of Great URL Design
28 November 2023 | 7:00 pm

Here’s Kyle Aster on why thoughtful URL design is important (in 2010):

URLs are universal. They work in Firefox, Chrome, Safari, Internet Explorer, cURL, wget, your iPhone, Android and even written down on sticky notes. They are the one universal syntax of the web. Don’t take that for granted.

I love this reminder of the ubiquity of URLs. They’re not just for typing into browser bars. They’re used in a plethora of ways:

  • As targets for scripting and scraping and other programmatic data retrieval.
  • As references, printed in the footnotes and appendixes of physical books.
  • As actionable triggers accessible via physical mediums, e.g. scannable QR codes or IoT device buttons.
  • And more!

When I reflect on examples of great URL design[1] I’ve encountered through the years — URLs that, when I saw them, I paused and thought “Wow, that’s really neat!” — these are a few that come to mind.

StackOverflow

StackOverflow was the first place I remember encountering URLs that struck a nice balance between the needs of computers and humans.

The URLs follow a pattern like this:

/questions/:id/:slug

:id is a unique identifier for the question that reveals nothing about the content. :slug, on the other hand, is a human-readable paraphrasing of the question that allows you to understand the question without ever actually going to the website.

The beauty is :slug is an optional parameter in the URL. For example:

stackoverflow.com/questions/16245767

Tells you nothing about the question being asked but it’s a valid URL that allows a server to easily find and serve that unique piece of content.

But StackOverflow also supports the :slug part of the URL which allows humans to quickly understand the contents living at that URL.

stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript/

As noted, the :slug is optional. It is not required by the server to find and serve the contents in question. In fact, it can easily be changed over time without without breaking the URL (which I find quite elegant).

Granted, it can also be used deceptively. For example, this is the same URL as above but it portends completely different contents (without breaking the link):

stackoverflow.com/questions/16245767/how-to-bake-a-cake

But hey, trade-offs in everything.

Slack

I remember when Slack launched a marketing campaign to educate people about the product. They used the language of the marketing campaign – “Slack is...” — in the page copy as well as in the URLs, e.g.

  • slack.com/is
  • slack.com/is/team-communication
  • slack.com/is/everything-in-one-place
  • slack.com/is/wherever-you-are

I remember being so intrigued at this effort to bring the design of the story-telling campaign all the way up into the URLs themselves.

Since then, I’ve always found delight in URLs that try to form natural languages sentences — slack.com/is/team-communication — rather than concatenate a series of hierarchical keywords — slack.com/product/team-communication.

Speaking of doing fun things with sentence structure in your URLs...

Jessica Hische

Jessica Hische has her website under a .is domain (which is for Iceland, apparently).

jessicahische.is

She riffs on this fun third-person form of “I am” across her site. For example, click on “About” in the primary navigation and it takes you to:

jessicahische.is/anoversharer

That’s fun! mydomain.com/about is clear too, but I love the whimsy of describing the “about” and doing it in sentence structure.

All of the nouns in her primary navigation follow this pattern, as well as her individual pieces of work. Like this writeup about one of her holiday culinary packaging gigs has the URL:

jessicahische.is/sofulloffancypopcorn

Fun!

URLs as Product

I’ve always loved services whose URLs map nicely to their domain semantics. For example, GitHub’s URLs map really well to git semantics like the three dot diff comparison in git:

/:owner/:project/compare/ref1...ref2

e.g.

github.com/django/django/compare/4.2.7...main

For technical products, this ability to navigate a website without necessarily seeing the user interface is a cool superpower.

NPM is somewhat similar. Want to see react-router on NPM? You don’t have to go to NPM’s home page and click around or use their search box. Once you become familiar with their site structure, you know you can lookup a package using:

/package/:package-name

e.g.

npmjs.com/package/react-router

Want to lookup a specific version of a package?

/package/:package-name/v/:semver

e.g.

npmjs.com/package/react-router/v/5.3.4

These kinds of shortcuts are super useful when you’re using a particular product. In the case of NPM, you’re hunting through your package.json and need to lookup some details of a specific package pinned at a specific version, you can navigate to NPM’s details of that package by merely identifying the version you want and typing the details into a URL bar.

NPM CDNs like unpkg do a good job at following these semantics as well. Want a file from a published package? The homepage of unpkg says:

unpkg.com/:package@:version/:file

In cases like this, the URL can be the product itself which makes its design all the more vital[2].

What’s Yours?

These are a few examples of URLs I’ve enjoyed using or seeing over the years. I’m sure there are others, but I’d be curious to know what your favorites are? Blog ’em!


  1. I haven’t found a lot of great resources on “great URL design”. This article by Alex was pretty good. I almost wish there was a “Dribble” for URL design. Just people showing off great URLs. For anyone with the ambition, url-gallery.com is available…
  2. Michael Jackson, creator of unpkg, pointed out: “It amazes me that unpkg has become as popular as it is considering that every URL anyone has ever used with it was crafted by hand. There is no search box.” That is amazing indeed! unpkg is very popular: 50 billion requests in a month in Sept-Oct of 2020.



Width and Height in CSS
20 November 2023 | 7:00 pm

In his video “Secret Mechanisms of CSS”, Josh explains (among other things) how width and height work in CSS. I loved his explanation so much, I am going to re-write it here for my own benefit. Hopefully the next time I have to explain it — to someone else, or to myself in my head — I’ll be able to do it as clearly as Josh.

The secret is in this summation: to determine width you look up the tree, to determine height you look down the tree.

For example, take this code:

<html>
  <body>
    <div>My content</div>
    <style>
      div {
        width: 100%;
        height: 100%;
      }
    </style>
  </body>
</html>

width looks up the tree and fills the space made available by its parent. In this case it will look all the way up to the <body> tag which looks to the <html> tag which, by default, has the width of the document. So the <div> does too.

Conversely, height looks at its children. It asks, “How big are all the elements inside of me?” and then automatically fills that height.

  • width: how wide are the things I am inside of?
  • height: how tall are the things inside of me?

The key insight here is: height: 100% means “as tall as all the things inside of me”, not “as tall as all the things I am inside of”.

That is why width: 100% does what a lot of people naturally expect: it goes the full width of the screen. Whereas height: 100% doesn’t do what people often expect and go the full height of the screen.

Again, width looks outward while height looks inward.

To be honest, I struggled with this idea for many years. Even when I knew width: 100% would give me what I wanted and height: 100% wouldn’t, I never fully understood why. Josh’s explanation is a great way to think about it. As he notes in his talk:

That's the funny thing about CSS. You can have a mental model that is 95% correct and go through your career with this slight gap [in understanding] that you never notice until one day it doesn’t line up and then there goes your afternoon.

So true.




HTML Web Components: An Example
15 November 2023 | 7:00 pm

In my article on HTML web components, I said:

But the unique power of web components (in the browser) is that they can render before JavaScript. React components cannot do this — full stop.

There’s a lot in there I wanted to explain more in-depth, but I just never go to it.

Then a reader kindly emailed me and asked:

what do you mean? As far as I know, web components need a JS API to be defined. How can they run before JS?

That is a great question. Allow me to explain my thinking.

There are different ways to use web components.

One way of using them requires JavaScript in order for them to “run” and display anything in the browser.

An alternate way of using them allows you to display information/content in the browser before any JavaScript executes, and then you use web component structured JavaScript[1] to enhance what’s already on display.

In an attempt to explain this more, let’s use an simplified example.

Imagine we want to build a component for displaying a user’s avatar. It will be an image and, when hovered, it will display a tooltip that shows their full name.

Screenshot of a profile photo with a mouse cursor hovering over it and dispaying a name.

If you were building this in React, you could imagine a typical implementation looking something like this:

import Tooltip from "3rd-party-tooltip-on-npm";

function UserAvatar({ src, name }) {
  const [showTooltip, setShowTooltip] = useState(false);
  return (
    <div>
      <img
        src={src}
        alt={`Profile photo of ${name}`}
        width="32"
        height="32"
      />
      {showTooltip && <Tooltip>{name}</Tooltip>}
    </div>
  )
}

/**
 * Example usage:
 *
 * <UserAvatar
 *   src="https://www.jim-nielsen.com/.well-known/avatar"
 *   name="Jim Nielsen"
 * />
 */

This is nice enough. But the point is: it requires JavaScript (React, 3rd-party-tooltip-on-npm, your JS code) before anything will render in the browser. It’s an all-or-nothing approach. If any JavaScript fails to download, parse, and run, the end user will see absolutely nothing. This is what it will look like:

Screenshot of a mouse cursor on a blank white screen.

Now, since this is a “component” and the web has components, couldn’t you port it to a web component and remove the dependency on React? Yes, you could. (Note: this code is not meant to be production ready, but illustrative.)

<user-avatar
  src="https://www.jim-nielsen.com/.well-known/avatar"
  name="Jim Nielsen"
></user-avatar>

<script>
  class UserAvatar extends HTMLElement {
    connectedCallback() {
      const src = this.getAttribute("src");
      const name = this.getAttribute("name");
      this.innerHTML = `
        <div>
          <img
            src="${src}"
            alt="Profile photo of ${name}"
            width="32"
            height="32"
          />
          <!-- Markup/code for the tooltip -->
        </div>
      `;
    }
  }
  customElements.define('user-avatar', UserAvatar);
</script>

You can see we have a web component that does something very similar to the React component but it doesn’t require React.

It does, however, still require JavaScript. If the user’s browser fails to download, parse, and run any JavaScript, nothing will display on screen. This is what they’ll see:

Screenshot of a mouse cursor on a blank white screen.

This kind of approach to building web components, per the original question, has a dependence on JavaScript. This web component cannot display anything on screen without JavaScript. It is what I meant in my original article when I said “JavaScript web components”.

But there’s another approach to building web components.

This approach is what Jeremy called “HTML web components”. It does not require JavaScript in order to provide basic functionality. But it does require the author of the component to step back and ask, “What am I trying to build here and how can I do it in a way that progressively enhances the end user’s experience?”

What we want to show to is a profile picture of the user. That shouldn’t require JavaScript, it’s just an <img>.

Is there a way for us to provide the basic functionality of <user-avatar> so that, before JavaScript loads (if it loads at all), the user still sees something?

With an approach that considers the idea of HTML web components, you can! Starting with the HTML, you could write something like this:

<img
  src="https://www.jim-nielsen.com/.well-known/avatar"
  alt="Profile photo of Jim Nielsen"
  width="32"
  height="32"
  title="Jim Nielsen"
/>

Given this HTML, we have a profile photo with some meta information. Note the title attribute: this will display a tooltip on hover (in browsers that support it). So before any JavaScript has been downloaded, parsed, and run, a desktop browser could display something like:

Screenshot of a profile photo with a mouse cursor hovering over it displaying a browser-native tooltip.

Is it what we want our end result to look like? No. Will that tooltip work everywhere? No. Is this The Best Thing Ever™️? No.

But that’s not the point. The point is we’re starting with a baseline, core experience that will provide basic functionality and content to a wide array of user agents before any JavaScript is required.

Once you’ve done everything you can in vanilla HTML to provide core elements of your baseline experience, you can begin enhancing the existing markup with additional functionality.

This is where HTML web components shine. You can wrap that basic functionality in a custom element and then, using the JavaScript APIs of web components, enhance the markup.

<user-avatar>
  <img
    src="https://www.jim-nielsen.com/.well-known/avatar"
    alt="Profile photo of Jim Nielsen"
    width="32"
    height="32"
    title="Jim Nielsen"
  />
</user-avatar>
<script>
  class UserAvatar extends HTMLElement {
    connectedCallback() {
      // Get the data for the component from exisiting markup
      const $img = this.querySelector("img");
      const src = $img.getAttribute("src");
      const name = $img.getAttribute("title");

      // Create the markup and event listeners for tooltip...

      // Append it to the DOM
      this.insertAdjacentHTML(
        'beforeend', 
        '<!-- code for tooltip here -->'
      );
    }
  }
  customElements.define('user-avatar', UserAvatar);
</script>

Again, this code is for illustration purposes, but it shows how you can leverage HTML to provide the basic functionality of a component and then you enhance it with JavaScript.

Now you have something that works pretty decent. It displays something before JavaScript without any layout shift[2]. And if/when JavaScript loads, you get that nice experience we wanted from the get go.

Screenshot of a basic HTML profile photo with a `title` tooltip on the left, and an enhanced tooltip with a profile photo on the right.

Hopefully this answers the question of, “I thought web components required JavaScript? How can they run before JavaScript?”

It’s all about approach. Web components give you an approach of augmentation, where you can provide basic functionality and then progressively enhance the experience depending on the end user’s capabilities.

Endnote: I know, I know, there’s a whole lot more that could be said about all the above. For example, a JS framework like Remix allows you to do something very similar to the above: provide basic HTML over the wire and then enhance (i.e. hydrate) that experience with JavaScript (i.e. React). RSC looks like it’ll do something similar — but that’s all a discussion for another day.


  1. I wrote about how I used custom elements to structure my JavaScript and enhance markup. Then Eric wrote a thoughtful, lengthy post on the same idea, but going way, way deeper into how web components can serve to structure and enhance your HTML, CSS, and JS.
  2. Zach’s talk is a great introduction to this idea of building web components in a way that delivers something useful in the initial HTML while also preventing layout shift as JavaScript enhances the markup with additional functionality — a very important UX detail IMO.




More News from this Feed See Full Web Site