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:
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 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.
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 has her website under a .is
domain (which is for Iceland, apparently).
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:
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!
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].
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!
url-gallery.com
is available… ↩
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.
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.
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:
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></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:
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:
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.
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.