Skip to main content

Journal

Design-ish systems (by Ethan Marcotte)

Here’s an interesting new article from Ethan Marcotte, in which he muses on better ways to think about Design Systems based on his recent experience.

Once you’ve identified the root causes, you’ll be in a far, far better place to choose the right things — and, more importantly, to create a system that finally supports your design.

Here’s what I took from it:

We can consider the difference between things, issues and broader goals.

It’s easy to focus on the things (design tokens, choice of design tool, programming language).

But things are just elements of the system; they don’t solve design systems.

Focusing too much or too early on the things leads to a tendency to patch problems.

Whereas discussing the current wider organisational issues we need to address leads to defining broader goals.

This in turn helps us choose and focus on the right things for the right reasons.

Diffchecker - Online diff tool

Diffchecker is a diff tool to compare text differences between two text files.

I had cause to use this free diff tool recently to compare two large minified CSS files and it did the trick better than any others I’ve tried. Thumbs up!

Building a resilient frontend using progressive enhancement (on GOV.UK)

GOV.UK’s guidance on developing using progressive enhancement is pretty great in all departments. It begins with this solid advice:

you should start by making your page work with just HTML, before adding anything else like Cascading Style Sheets (CSS) and JavaScript. This is because HTML is the most resilient layer. If the HTML fails there’s no web page. Should the CSS or JavaScript fail, the HTML will still render correctly.

I particularly like the section where they address the misconception that a resilient baseline is only required in places where the user has explicitly disabled JavaScript and therefore not worth worrying about.

You should not assume the reason for designing a service that works without CSS or JavaScript is because a user chooses to switch these off. There are many situations when extra layers can fail to load or are filtered.

As their subsequent list of scenarios illustrates, a user turning JavaScript off is probably the least likely of a range of reasons why extra layers on top of HTML can fail.

Relatedly, I’ve often found that Everyone has JavaScript, right? serves as a great go-to reference for these sorts of conversations around resilience.

I’ve started reading Recursion by Blake Crouch.

Encapsulated Eleventy/Nunjucks components with macros (by Trys Mudford)

Trys shows us how to use the Nunjucks macro to create encapsulated components. This works out less leaky and more predictable than an include preceded by variables assigned with set.

Trys’s solution allows us to render components like so:

{{ component('button', {
  text: 'Press me'
}) }}

{# Output #}
<button type="button">Press me</button>

Update, 8th Aug 2021: when I tried implementing this I found that it results in errors when attempting to render components anywhere other than on the base template where Trys recommended including the import line. The workaround—as Paul Salaets points out—is to include the import at the top of every page-level template (index.njk, archive.njk etc) that uses the component macro.

In Praise of the Unambiguous Click Menu (on CSS-Tricks)

Mark Root-Wiley explains why navigation menus that appear on click rather than hover are better.

I like the fact that it calls out that:

When you first make this change, it’s true that some visitors might still expect hover menus. They may even say they prefer them if you ask.

But then goes on to provide some rationale (ammunition?) from various big guns on why click menus are better.

From the US Web Design System:

Avoid using hover to expand dropdown lists. Hover is difficult for some users and won’t work on touch screens. Dropdowns should expand on click or with keyboard navigation.

From popular frontend framework Bootstrap:

What it really boils down to is user intent. The purpose of a hover state is to indicate something is clickable (underlined text). The purpose of a click is to actually do something, to take an explicit action. Opening a dropdown is an explicit action and should only happen on click.

(via @jamesmockett)

How to Favicon in 2021 (on CSS-Tricks)

Some excellent favicon tips from Chris Coyier, referencing Andrey Sitnik’s recent article of the same name.

I always appreciate someone looking into and re-evaluating the best practices of something that literally every website needs and has a complex set of requirements.

Chris is using:

<link rel="icon" href="/favicon.ico"><!-- 32x32 -->
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- 180x180 -->
<link rel="manifest" href="/manifest.webmanifest">

And in manifest.webmanifest:

{
  "icons": [
    { "src": "/192.png", "type": "image/png", "sizes": "192x192" },
    { "src": "/512.png", "type": "image/png", "sizes": "512x512" }
  ]
}

(via @mxbck)

How I subset web fonts

On my personal website I currently use three web fonts from the Source Sans 3 group: regular, italic and semibold. I self-host my fonts because that’s a good practice. Additionally I use a variety of special characters to add some typographic life to the text.

When self-hosting it’s important from a performance perspective to minimise the weight of the font files your visitors must download. To achieve this I subset my fonts so as to include only the characters my pages use but no more. Here’s how I do it.

Note: to follow these steps, you’ll need to install glyphhanger. The Github page includes installation and usage guidelines however there are a few common installation pitfalls so if you’re on a Mac and run into trouble I recommend checking Sara Soueidan’s How I set up Glyphhanger on macOS to get you back on track.

For the purposes of this walkthrough I’ll assume you have a directory in your application named fonts.

Start by deleting any existing custom font files from your application’s fonts directory.

Run your site locally in an incognito browser window. For my Eleventy-based site, I run npm run serve which serves the site at http://localhost:8080.

Visually check your locally-running site to ensure that now you’ve deleted your web fonts it’s no longer serving them and is instead rendering text using system fonts.

Visit the source page for your custom fonts—in my case the Github repository for Source Sans 3. Download in .ttf format the latest versions of the fonts you need and place them in your fonts directory. For me these are:

  • Regular,
  • Italic; and
  • Semibold.

You’ll notice the large file sizes of these .ttf files. For example Source Sans 3’s Regular.ttf font is 299 kb.

At the command line, cd into your fonts directory.

Now we’re going to run glyphhanger on one font at a time. This fantastic tool will intelligenty crawl your website to check which glyphs are currently in use for the specified weight then include those in a subset file which it outputs in .ttf, .woff and .woff2 formats. I use glyphhanger’s spider option so that it spiders multiple pages (rather than just one) at a time, meaning that it is more likely to catch all the special characters I’m using.

glyphhanger http://localhost:8080/posts/ --subset=SourceSans3-Regular.ttf --spider-limit=0

If all went well you should see output like this:

U+20-23,U+25-2A,U+2C-5B,U+5D,U+5F,U+61-7D,U+A9,U+B7,U+BB,U+D7,U+E9,U+F6,U+200B,U+200E,U+2013,U+2014,U+2018,U+2019,U+201C,U+201D,U+2026,U+2122,U+2190,U+2192,U+2615,U+FE0F
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.ttf (was 292.24 KB, now 46.99 KB)
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.zopfli.woff (was 292.24 KB, now 22.14 KB)
Subsetting SourceSans3-Regular.ttf to SourceSans3-Regular-subset.woff2 (was 292.24 KB, now 17.77 KB)

The .woff2 subset file has reduced the file size from 299 kb to 17.77 kb which is pretty impressive!

Update your CSS to point at the new woff2 and woff subset files for your font. My updated CSS looks like this:

@font-face {
  font-family: Source Sans Pro;
  src: url(/fonts/sans/SourceSans3-Regular-subset.woff2) format("woff2"),
       url(/fonts/sans/SourceSans3-Regular-subset.zopfli.woff) format("woff");
  font-weight: 400;
  font-display: swap;
}

Check your locally running application to ensure that the relevant text (body copy in this case) is now being served using the web font rather than fallback font, and that special characters are also being served using the web font.

I’ll usually crack open the Fonts panel in Firefox’s DevTools and check that, amongst other things, my pagination links which use the rightward pointing arrow character (→ or unicode U+2192) are rendering it using Source Sans Pro and not sticking out like a sore thumb by using Helvetica due to the glyph not being present in the subset.

Delete the .ttf file you started with and any .ttf subsets generated, because you won’t serve files in that format to your website visitors.

Repeat the glyphhanger subsetting and CSS updating process for any other weights (italic, semibold) or custom fonts you want to subset.

One last handy tip: if there’s a weight for which I don’t need a fancy character set (for example the Semibold I use for headings), I might just grab default latin charset woff and woff2 files from the Google Webfonts Helper. The files tend to be small and well-optimised and this can save a little time. (This is only possible if the font is available from Google Fonts which is true in the case of Source Sans 3.)

Images on the Web: The Big Picture, Part 1

In modern web development there are a myriad ways to present an image on a web page and it can often feel pretty baffling. In this series I step through the options, moving from basic to flexible images; then from modern responsive images to the new CSS for fitting different sized images into a common shape. By the end I’ll arrive at a flexible, modern boilerplate for images.

Scope

This article is primarily about the HTML img element (and related markup). I might mention CSS background images at some point, but by and large I’m focusing on images as content rather than decoration.

Similarly I probably won’t mention icons at all. I see them as a separate concern and recommend you use inline SVG rather than any image-based approach.

Terminology

Replaced element

The image element is a replaced element which means that the element is replaced by the resource (file) referenced in its src attribute.

Aspect Ratio

You get an image’s aspect ratio by dividing its width by its height.

A 160px wide × 90px tall image can be represented as 16:9, or 1.777.

Aspect Ratio is an intrinsic characteristic of an image—i.e. it is “part of the image”—therefore outside of our control as developers. We can apply extrinsic settings which change the dimensions of the rendered image on our web page, however its aspect ratio was determined when the image was originally created and cropped.

Assumptions

CSS-wise, assume that we’ll start with nothing more complex than the following boilerplate:

html {
  box-sizing: border-box;
}

*, *:before, *:after {
  box-sizing: inherit;
}

img {
  border-style: none;
  display: block;
}

A basic image

Let’s start by going back to basics. I can include an image on a web page like so:

<img src="/img/250x377.jpg" alt="A Visit… by Jennifer Egan" />

Note that our markup contains no width or height attributes, just an alt for accessibility. With no size-related attributes (and in the absence of any CSS acting on its width or height) the image simply displays at its intrinsic dimensions i.e. the dimensions at which the file was saved, in this case 250 × 377 pixels. The image is output as follows:

“A Visit from the Goon Squad“ by Jennifer Egan

Now I know that narrow and static images like this feel pretty old-school. Since the Responsive Web Design movement we’re more accustomed to seeing full-column-width media and complex markup for intelligently selecting one file from many options.

However I still occasionally encounter use cases for displaying a relatively narrow image as-is.

Sticking with the book image example, given its aspect ratio you probably wouldn’t want it to be full-column-width on anything other than the narrowest screens simply because of how tall it could become at the expense of the reading experience. You might also be loading your images from a third party bookshop with which you have an affiliate scheme, and therefore have little control over file size and other factors influencing performance and responsive behaviour. As such you might do well to keep things simple and just load a sensibly-sized thumbnail.

See also this figure illustrating a simple database schema on the Ruby on Rails Tutorial website. On wide viewports, the author’s preference is to simply display the image at its natural, small size and centre it rather than blowing it up unnecessarily.

In summary, there remain times when you might need a narrow, fixed-size image so I want to keep that option open.

Include size attributes

When we know the dimensions of our image in advance, we can improve upon our previous markup by explicitly adding the width and height attributes.

<img src="/img/250x377.jpg" width="250" height="377" alt="" />

Don’t expect fireworks; this renders the image exactly as before.

“A Visit from the Goon Squad“ by Jennifer Egan

However, this addition allows the browser to reserve the appropriate space in the page for the image before it has loaded. If we don’t do this, we risk a situation where text immediately after our image renders higher up the page than it should while the image is loading, only to shift jarringly after the image loads.

We’re not overriding width or height with CSS (remember CSS rules would trump HTML attributes because attributes have no specificity) so the browser will render the image at whatever size attribute values we provide, regardless of the image’s intrinsic dimensions. As such, to avoid squashing and distortion we would ensure that our size attribute values matched the image’s real dimensions.

Flexible Images

At the dawn of the mobile web, many authors sought to handle mobile devices by creating a separate, dedicated mobile website. However this meant duplication of cost, code, content and maintenance. Responsive Web Design provided a counterpoint with a “create once, publish everywhere” philosophy which embraced the fluidity of the web and suggested we could serve desktop and mobile devices alike from a single codebase.

RWD proposed making layout and content adaptable. This included the idea of flexible images—that for any given image you need just a single, wide version plus some clever CSS to enable it to work not only on desktop but also to adapt its size for narrower contexts and still look good.

By default when a wide image is rendered within a narrower container it will overflow that container, breaking the layout. The key to avoiding this is to set a tolerance for how wide the image is permitted to go. Generally, we’ll tolerate the image being 100% as wide as its container but no wider. We can achieve this using max-width.

<img src="/img/wide.png" alt="" />
img {
  max-width: 100%;
}

The eagle-eyed will have noticed that the above snippet once again excludes the HTML width and height attributes. That’s because when we began working responsively many of us stopped adding those size attributes, feeling that for flexible images the practice was redundant. The image’s dimensions were now a moving target so the space needing reserved by the browser while the image loaded was variable rather than fixed. And we were right: for a long time, browsers were not capable of reserving space for a moving target, so including the size attributes served no real purpose.

Regardless, some content management systems (most notably Wordpress) continued to output images with HTML width and height attributes as standard. This introduced a challenge. Without the attributes we could rely on the browser to take our simple max-width:100% declaration and also implicitly apply height:auto thereby always preserving the image‘s aspect ratio when scaling it down. To achieve the same goal when the HTML height attribute is present, we needed the following revised CSS:

img {
  max-width: 100%;
  height: auto;
}

Here’s an example of a flexible image. It’s 2000 pixels wide, but shrinks to fit inside its narrower parent. Magic!

Jank-free responsive images

There’s been a recent development wherein modern browsers can now reserve appropriate space for flexible images while they are loading (rather than only for fixed images).

This means that adding the width and height attributes is once again a good idea.

If you know the image’s aspect ratio in advance, you can now use any combination of width and height attribute values which represent that ratio, and the browser will dynamically calculate and reserve the appropriate required space in the layout while the image loads, once again avoiding those jarring layout shifts I mentioned before.

However this presents a couple of challenges.

Firstly, having the height HTML attribute once again means that for any image we want flexibly scaled and safely constrained by CSS max-width, we’ll also need to override that explicit height attribute value with CSS.

Secondly, having the width attribute can be problematic when the image is one which we explicitly want to be full-container-width, such as the featured image in a blog post. The problem arises when the width attribute value is less than the containing element’s current width. If the only CSS you have on your image is max-width:100% then the image will adopt the value from its width attribute and consequently be narrower than its parent, ruining the effect. One approach might be to always use a sufficiently high width value but that feels a tad brittle; I’d rather employ a solution that is more explicit and decisive.

To solve both of the above challenges, we can apply some additional CSS.

  /*
  Ensure correct aspect ratio is preserved when
  max-width: 100% is triggered and image
  has the HTML height attribute set,
  while doing no harm otherwise.
  */
img[height] {
  height: auto;
}

  /*
  Optional class to make an image 100% container-width.
  Overrides the 'width' attribute, avoiding the risk of the image
  being too narrow because its width value is narrower than the container.
  When using this try to ensure your image’s intrinsic width is at least as
  wide as its container’s maximum width because otherwise on wide
  viewports the image would stretch and the results might not be great.
  */
.u-full-parent-width {
  width: 100%;
}

Pros and cons of the “one large image” approach

I’d like to quickly take stock.

Let’s say we have a source image which is 1200px wide. Let’s also say that it’s the featured image for a blog post and therefore will be placed in our main content column, and that this column is never wider than 600px.

If we make the image flexible using max-width:100%, it’ll work on wide viewports and narrow viewports (such as a mobile phone) alike.

On the plus-side, we only need to create one image for our blog post and we’re done.

Another positive is that on devices with retina screens—capable of displaying a comparatively greater density of pixel information in the same physical space—our oversized image will appear at higher quality and look great.

On the downside, we are delivering a much larger image and therefore file size than is required for, say, a 320px wide context. This has performance implications since bigger files mean longer download times, and this is exacerbated when the device is not connected to high-speed wifi (as is often the case with a mobile phone).

Another issue which is perhaps less obvious is that your content may not be read on your website with your CSS operating on it. For example if your website has an RSS feed (like mine does) then someone may well be reading your article in another environment (such as a feed reader application or website) and who is to say how that oversized image will look there?

Dealing with these challenges using modern Responsive Images will be the subject of Part #2.

References

External Link Bookmark Note Entry Search