Web Standards

5 Accessibility Quick Wins You Can Implement Today

Css Tricks - Wed, 02/23/2022 - 5:15am

Let’s face it: building an AA or AAA-accessible product can be quite daunting. Luckily, having an accessible product isn’t all-or-nothing. Even seemingly small improvements can have nice quality of life benefits for many people.

In that spirit, here are five accessibility quick wins you can implement today.

Quick Win 1: Indicate the Current Page

It’s probably safe to assume that a different style is the most common way to communicate the current page of a site or app. However, even if those styles are clear and with great contrast ratios, they’re still only a visual cue.

So what happens if a person with limited vision cannot see that separation? How will they know what page they’re on?

Creating an accessible product is to ensure its markup communicates as clearly as its design.

Adding aria-current="page" to the active navigation element is one way to ensure markup and design communicate the same information with or without assistive technologies.

<a aria-current="page" href="/">Home</a> &#x1f389; Bonus

Use CSS attribute selectors to style the aria-current="page" element to keep the visual and markup cues linked.

[aria-current="page"] { /* Active element styles */ } Quick Win 2: Document Language

While some people can visit a website and determine the language or locale of its content, not all people have that luxury. Again, markup must communicate the same information as the visual design — even if that information may seem implied.

Add the lang attribute to the <html> tag to communicate not only the document’s language, but its locale. This will help assistive technologies like screen readers understand and communicate the content. Even if the app only supports one language, this can be a nice quality of life improvement for many people.

<html lang="en-US">

For apps which support multiple languages, the <html> element is likely not the only one to need its lang value defined. Use the lang attribute on specific elements whose language differs from the rest of the document, like links within a language toggle menu. In this case, pair the use of lang with the hreflang attribute to not only communicate the language of the link itself, but also of its destination.

<a lang="fi" hreflang="fi" href="/" title="Suomeksi"> <bdi>Suomeksi</bdi> </a> Quick Win 3: Use prefers-reduced-motion

Whether drawing attention to actions or updates, or creating a sense of life and charm, adding motion to an app can really elevate its experience. However, some people may find that experience disorienting.

Windows and MacOS both offer a setting at the OS level for people to greatly reduce the amount of motion when using their systems. The prefers-reduced-motion setting can greatly improve the experience on a computer, but it does not extends beyond the UI of the operating system. So wouldn’t it be nice if our apps could respect that same system setting and provide a more static experience for those who prefer it?

Well, with CSS media queries, they can.

The prefers-reduced-motion media query can be used to greatly reduce or remove all motion from an app whenever the system setting is enabled.

CodePen Embed Fallback @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }

The blanket approach shown here prevents all motion, but it can leave little room for nuance. It’d be best to review the needs of those using the product, but consider these other options as well.

One approach could be to only animate one property at a time in prefers-reduced-motion settings. So consider a <Modal /> that fades and scales into view with opacity and transform. In reduced motion environments, only the opacity would transition. The scaling effect would be removed as they are more commonly problematic than fading.

Another option could be to look at the prefers-reduced-motion environment a bit more literally and remove all motion. This would do away with our scaling modals, sliding drawers, and bouncing notifications, but would still leave room for color transitions on links and buttons.

Quick Win 4: Indicate Data Sorting State

A common theme across all of these tips is to ensure that an app’s visual design and markup communicate the same things. So, when the design uses an arrow to indicate the sort direction of a table column, how can that also be communicated in the markup?

Setting the aria-sort attribute to ascending /descending on the header of the actively-sorted column allows the markup to communicate the same content structure as a visual indicator in the UI.

This will help ensure that people using assistive technologies and those who aren’t can understand the content in the same way.

<thead> <tr> <th>First Name</th> <th aria-sort="ascending">Last Name</th> </tr> </thead> Quick Win 5: Lazy Loading Lists

Whether scrolling through an endless stream of tweets or through an impossible-to-decide list of products, the web has fully embraced lazy loading long lists of data (and alliteration, apparently).

This is when the aria-setsize and aria-posinset attributes become very valuable. While a person’s progression through the list can be communicated visually in many different ways, these attributes are used to communicate that same progression to many assistive technologies.

As developers, we likely have access to the length of an entire list as well as the index of the current items being displayed. With that, the aria-setsize attribute would define the total length of the list, while the aria-posinset attribute would define an item’s specific position (or index) within that list.

If the total length of the list is not known, then aria-setsize should be set to -1.

With these attributes, assistive technologies can better interpret a list and a person can better understand their position within it.

<h2 id="top-artists-title">Top Artists of 2021</h2> <ul role="listbox" aria-labelledby="top-artists-title"> <li role="option" aria-setsize="20" aria-posinset="5">Bloodbound</li> <li role="option" aria-setsize="20" aria-posinset="6">Manimal</li> <li role="option" aria-setsize="20" aria-posinset="7">Powerwolf</li> </ul>

Take a listen to how these attributes are announced using MacOS VoiceOver.

&#x1f389; Bonus Win: Axe-DevTools Extension

Implementing those five accessibility quick wins is a great start, but that’s exactly what it is —a start. There’s a sprawling landscape of assistive technologies and sets of abilities a person can posses, and navigating it all alone can feel overwhelming.

Fortunately, there are plenty of tools to help with auditing a product’s accessibility that make the journey much more manageable. My personal favorite — my trusty accessibility compass — is the Axe-DevTools browser extension.

Running the Axe-DevTools accessibility scanner can return tons of valuable information. Not only will it display all issues and warnings found on the page, but it groups them by approximate severity. It can also highlight the element on the page or in the Elements tab and provide links to learn more about the specific issue.

However, most importantly, it will offer clear and concise approaches to fix the specific issue.

Wrapping Up

A product isn’t made accessible overnight; nor is a product’s accessibility work ever really complete. Like anything else on the web, accessibility evolves and requires maintenance. However, even seemingly small additions can have an impact on a product’s accessibility and a person’s overall experience.

After stepping into a new codebase, these are often some of the first few things I look into — some “low-hanging fruit” of accessibility, if you will.

Reaching AAA or even AA conformance can feel like scaling an 8,000 meter peak. These steps won’t carry you to the summit, but an expedition is never completed in a single stride.


5 Accessibility Quick Wins You Can Implement Today originally published on CSS-Tricks. You should get the newsletter.

Reliably Send an HTTP Request as a User Leaves a Page

Css Tricks - Tue, 02/22/2022 - 5:24am

On several occasions, I’ve needed to send off an HTTP request with some data to log when a user does something like navigate to a different page or submit a form. Consider this contrived example of sending some information to an external service when a link is clicked:

<a href="/some-other-page" id="link">Go to Page</a> <script> document.getElementById('link').addEventListener('click', (e) => { fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }) }); }); </script>

There’s nothing terribly complicated going on here. The link is permitted to behave as it normally would (I’m not using e.preventDefault()), but before that behavior occurs, a POST request is triggered on click. There’s no need to wait for any sort of response. I just want it to be sent to whatever service I’m hitting.

On first glance, you might expect the dispatch of that request to be synchronous, after which we’d continue navigating away from the page while some other server successfully handles that request. But as it turns out, that’s not what always happens.

Browsers don’t guarantee to preserve open HTTP requests

When something occurs to terminate a page in the browser, there’s no guarantee that an in-process HTTP request will be successful (see more about the “terminated” and other states of a page’s lifecycle). The reliability of those requests may depend on several things — network connection, application performance, and even the configuration of the external service itself.

As a result, sending data at those moments can be anything but reliable, which presents a potentially significant problem if you’re relying on those logs to make data-sensitive business decisions.

To help illustrate this unreliability, I set up a small Express application with a page using the code included above. When the link is clicked, the browser navigates to /other, but before that happens, a POST request is fired off.

While everything happens, I have the browser’s Network tab open, and I’m using a “Slow 3G” connection speed. Once the page loads and I’ve cleared the log out, things look pretty quiet:

But as soon as the link is clicked, things go awry. When navigation occurs, the request is cancelled.

And that leaves us with little confidence that the external service was actually able process the request. Just to verify this behavior, it also occurs when we navigate programmatically with window.location:

document.getElementById('link').addEventListener('click', (e) => { + e.preventDefault(); // Request is queued, but cancelled as soon as navigation occurs. fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: 'data' }), }); + window.location = e.target.href; });

Regardless of how or when navigation occurs and the active page is terminated, those unfinished requests are at risk for being abandoned.

But why are they cancelled?

The root of the issue is that, by default, XHR requests (via fetch or XMLHttpRequest) are asynchronous and non-blocking. As soon as the request is queued, the actual work of the request is handed off to a browser-level API behind the scenes.

As it relates to performance, this is good — you don’t want requests hogging the main thread. But it also means there’s a risk of them being deserted when a page enters into that “terminated” state, leaving no guarantee that any of that behind-the-scenes work reaches completion. Here’s how Google summarizes that specific lifecycle state:

A page is in the terminated state once it has started being unloaded and cleared from memory by the browser. No new tasks can start in this state, and in-progress tasks may be killed if they run too long.

In short, the browser is designed with the assumption that when a page is dismissed, there’s no need to continue to process any background processes queued by it.

So, what are our options?

Perhaps the most obvious approach to avoid this problem is, as much as possible, to delay the user action until the request returns a response. In the past, this has been done the wrong way by use of the synchronous flag supported within XMLHttpRequest. But using it completely blocks the main thread, causing a host of performance issues — I’ve written about some of this in the past — so the idea shouldn’t even be entertained. In fact, it’s on its way out of the platform (Chrome v80+ has already removed it).

Instead, if you’re going to take this type of approach, it’s better to wait for a Promise to resolve as a response is returned. After it’s back, you can safely perform the behavior. Using our snippet from earlier, that might look something like this:

document.getElementById('link').addEventListener('click', async (e) => { e.preventDefault(); // Wait for response to come back... await fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: 'data' }), }); // ...and THEN navigate away. window.location = e.target.href; });

That gets the job done, but there are some non-trivial drawbacks.

First, it compromises the user’s experience by delaying the desired behavior from occurring. Collecting analytics data certainly benefits the business (and hopefully future users), but it’s less than ideal to make your present users to pay the cost to realize those benefits. Not to mention, as an external dependency, any latency or other performance issues within the service itself will be surfaced to the user. If timeouts from your analytics service cause a customer from completing a high-value action, everyone loses.

Second, this approach isn’t as reliable as it initially sounds, since some termination behaviors can’t be programmatically delayed. For example, e.preventDefault() is useless in delaying someone from closing a browser tab. So, at best, it’ll cover collecting data for some user actions, but not enough to be able to trust it comprehensively.

Instructing the browser to preserve outstanding requests

Thankfully, there are options to preserve outstanding HTTP requests that are built into the vast majority of browsers, and that don’t require user experience to be compromised.

Using Fetch’s keepalive flag

If the keepalive flag is set to true when using fetch(), the corresponding request will remain open, even if the page that initiated that request is terminated. Using our initial example, that’d make for an implementation that looks like this:

<a href="/some-other-page" id="link">Go to Page</a> <script> document.getElementById('link').addEventListener('click', (e) => { fetch("/log", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ some: "data" }), keepalive: true }); }); </script>

When that link is clicked and page navigation occurs, no request cancellation occurs:

Instead, we’re left with an (unknown) status, simply because the active page never waited around to receive any sort of response.

A one-liner like this an easy fix, especially when it’s part of a commonly used browser API. But if you’re looking for a more focused option with a simpler interface, there’s another way with virtually the same browser support.

Using Navigator.sendBeacon()

The Navigator.sendBeacon()function is specifically intended for sending one-way requests (beacons). A basic implementation looks like this, sending a POST with stringified JSON and a “text/plain” Content-Type:

navigator.sendBeacon('/log', JSON.stringify({ some: "data" }));

But this API doesn’t permit you to send custom headers. So, in order for us to send our data as “application/json”, we’ll need to make a small tweak and use a Blob:

<a href="/some-other-page" id="link">Go to Page</a> <script> document.getElementById('link').addEventListener('click', (e) => { const blob = new Blob([JSON.stringify({ some: "data" })], { type: 'application/json; charset=UTF-8' }); navigator.sendBeacon('/log', blob)); }); </script>

In the end, we get the same result — a request that’s allowed to complete even after page navigation. But there’s something more going on that may give it an edge over fetch(): beacons are sent with a low priority.

To demonstrate, here’s what’s shown in the Network tab when both fetch() with keepalive and sendBeacon() are used at the same time:

By default, fetch() gets a “High” priority, while the beacon (noted as the “ping” type above) have the “Lowest” priority. For requests that aren’t critical to the functionality of the page, this is a good thing. Taken straight from the Beacon specification:

This specification defines an interface that […] minimizes resource contention with other time-critical operations, while ensuring that such requests are still processed and delivered to destination.

Put another way, sendBeacon() ensures its requests stay out of the way of those that really matter for your application and your user’s experience.

An honorable mention for the ping attribute

It’s worth mentioning that a growing number of browsers support the ping attribute. When attached to links, it’ll fire off a small POST request:

<a href="http://localhost:3000/other" ping="http://localhost:3000/log"> Go to Other Page </a>

And those requests headers will contain the page on which the link was clicked (ping-from), as well as the href value of that link (ping-to):

headers: { 'ping-from': 'http://localhost:3000/', 'ping-to': 'http://localhost:3000/other' 'content-type': 'text/ping' // ...other headers },

It’s technically similar to sending a beacon, but has a few notable limitations:

  1. It’s strictly limited for use on links, which makes it a non-starter if you need to track data associated with other interactions, like button clicks or form submissions.
  2. Browser support is good, but not great. At the time of this writing, Firefox specifically doesn’t have it enabled by default.
  3. You’re unable to send any custom data along with the request. As mentioned, the most you’ll get is a couple of ping-* headers, along with whatever other headers are along for the ride.

All things considered, ping is a good tool if you’re fine with sending simple requests and don’t want to write any custom JavaScript. But if you’re needing to send anything of more substance, it might not be the best thing to reach for.

So, which one should I reach for?

There are definitely tradeoffs to using either fetch with keepalive or sendBeacon() to send your last-second requests. To help discern which is the most appropriate for different circumstances, here are some things to consider:

You might go with fetch() + keepalive if:
  • You need to easily pass custom headers with the request.
  • You want to make a GET request to a service, rather than a POST.
  • You’re supporting older browsers (like IE) and already have a fetch polyfill being loaded.
But sendBeacon() might be a better choice if:
  • You’re making simple service requests that don’t need much customization.
  • You prefer the cleaner, more elegant API.
  • You want to guarantee that your requests don’t compete with other high-priority requests being sent in the application.
Avoid repeating my mistakes

There’s a reason I chose to do a deep dive into the nature of how browsers handle in-process requests as a page is terminated. A while back, my team saw a sudden change in the frequency of a particular type of analytics log after we began firing the request just as a form was being submitted. The change was abrupt and significant — a ~30% drop from what we had been seeing historically.

Digging into the reasons this problem arose, as well as the tools that are available to avoid it again, saved the day. So, if anything, I’m hoping that understanding the nuances of these challenges help someone avoid some of the pain we ran into. Happy logging!

Reliably Send an HTTP Request as a User Leaves a Page originally published on CSS-Tricks. You should get the newsletter.

A Complete Guide to CSS Cascade Layers

Css Tricks - Mon, 02/21/2022 - 5:40am

This is your complete guide to CSS cascade layers, a CSS feature that allows us to define explicit contained layers of specificity, so that we have full control over which styles take priority in a project without relying on specificity hacks or !important. This guide is intended to help you fully understand what cascade layers are for, how and why you might choose to use them, the current levels of support, and the syntax of how you use them.

Table of Contents Quick example /* establish a layer order up-front, from lowest to highest priority */ @layer reset, defaults, patterns, components, utilities, overrides; /* import stylesheets into a layer (dot syntax represents nesting) */ @import url('framework.css') layer(components.framework); /* add styles to layers */ @layer utilities { /* high layer priority, despite low specificity */ [data-color='brand'] { color: var(--brand, rebeccapurple); } } @layer defaults { /* higher specificity, but lower layer priority */ a:any-link { color: maroon; } } /* un-layered styles have the highest priority */ a { color: mediumvioletred; } Introduction: what are cascade layers?

CSS Cascade Layers are intended to solve tricky problems in CSS. Let’s take a look at the main problem and how cascade layers aim to solve it.

Problem: Specificity conflicts escalate

Many of us have been in situations where we want to override styles from elsewhere in our code (or a third-party tool), due to conflicting selectors. And over the years, authors have developed a number of “methodologies” and “best practices” to avoid these situations — such as “only using a single class” for all selectors. These rules are usually more about avoiding the cascade, rather than putting it to use.

Managing cascade conflicts and selector specificity has often been considered one of the harder — or at least more confusing — aspects of CSS. That may be partly because few other languages rely on a cascade as their central feature, but it’s also true that the original cascade relies heavily on heuristics (an educated-guess or assumption built into the code) rather than providing direct and explicit control to web authors.

Selector specificity, for example — our primary interaction with the cascade — is based on the assumption that more narrowly targeted styles (like IDs that are only used once) are likely more important than more generic and reusable styles (like classes and attributes). That is to say: how specific the selector is. That’s a good guess, but it’s not a totally reliable rule, and that causes some issues:

  • It combines the act of selecting elements, with the act of prioritizing rule-sets.
  • The simplest way to ‘fix’ a conflict with specificity is to escalate the problem by adding otherwise unnecessary selectors, or (gasp) throwing the !important hand-grenade.
.overly#powerful .framework.widget { color: maroon; } .my-single_class { /* add some IDs to this ??? */ color: rebeccapurple; /* add !important ??? */ } Solution: cascade layers provide control

Cascade layers give CSS authors more direct control over the cascade so we can build more intentionally cascading systems without relying as much on heuristic assumptions that are tied to selection.

Using the @layer at-rule and layered @imports, we can establish our own layers of the cascade — building from low-priority styles like resets and defaults, through themes, frameworks, and design systems, up to highest-priority styles, like components, utilities, and overrides. Specificity is still applied to conflicts within each layer, but conflicts between layers are always resolved by using the higher-priority layer styles.

@layer framework { .overly#powerful .framework.widget { color: maroon; } } @layer site { .my-single_class { color: rebeccapurple; } }

These layers are ordered and grouped so that they don’t escalate in the same way that specificity and importance can. Cascade layers aren’t cumulative like selectors. Adding more layers doesn’t make something more important. They’re also not binary like importance — suddenly jumping to the top of a stack — or numbered like z-index, where we have to guess a big number (z-index: 9999999?). In fact, by default, layered styles are less important than un-layered styles.

@layer defaults { a:any-link { color: maroon; } } /* un-layered styles have the highest priority */ a { color: mediumvioletred; } Where do layers fit in the cascade?

The cascade is a series of steps (an algorithm) for resolving conflicts between styles.

html { --button: teal; } button { background: rebeccapurple !important; } .warning { background: maroon; } <button class="warning" style="background: var(--button);"> what color background? </button>

With the addition of cascade layers, those steps are:

Selector specificity is only one small part of the cascade, but it’s also the step we interact with most, and is often used to refer more generally to overall cascade priority. People might say that the !important flag or the style attribute “adds specificity” — a quick way of expressing that the style becomes higher priority in the cascade. Since cascade layers have been added directly above specificity, it’s reasonable to think about them in a similar way: one step more powerful than ID selectors.

However, CSS Cascade Layers also make it more essential that we fully understand the role of !important in the cascade — not just as a tool for “increasing specificity” but as a system for balancing concerns.

!important origins, context, and layers are reversed!

As web authors, we often think of !important as a way of increasing specificity, to override inline styles or highly specific selectors. That works OK in most cases (if you’re OK with the escalation) but it leaves out the primary purpose of importance as a feature in the overall cascade.

Importance isn’t there to simply increase power — but to balance the power between various competing concerns.

Important origins

It all starts with origins, where a style comes from in the web ecosystem. There are three basic origins in CSS:

  • The browser (or user agent)
  • The user (often via browser preferences)
  • Web authors (that’s us!)

Browsers provide readable defaults for all the elements, and then users set their preferences, and then we (authors) provide the intended design for our web pages. So, by default, browsers have the lowest priority, user preferences override the browser defaults, and we’re able to override everyone.

But the creators of CSS were very clear that we should not actually have the final word:

If conflicts arise the user should have the last word, but one should also allow the author to attach style hints.

— Håkon Lie (emphasis added)

So importance provides a way for the browser and users to re-claim their priority when it matters most. When the !important flag is added to a style, three new layers are created — and the order is reversed!

  1. !important browser styles (most powerful)
  2. !important user preferences
  3. !important author styles
  4. normal author styles
  5. normal user preferences
  6. normal browser styles (least powerful)

For us, adding !important doesn’t change much — our important styles are pretty close to our normal styles — but for the browser and user it’s a very powerful tool for regaining control. Browser default style sheets include a number of important styles that it would be impossible for us to override, such as:

iframe:fullscreen { /* iframes in full-screen mode don't show a border. */ border: none !important; padding: unset !important; }

While most of the popular browsers have made it difficult to upload actual user stylesheets, they all offer user preferences: a graphic interface for establishing specific user styles. In that interface, there is always a checkbox available for users to choose if a site is allowed to override their preferences or not. This is the same as setting !important in a user stylesheet:

Important context

The same basic logic is applied to context in the cascade. By default, styles from the host document (light DOM) override styles from an embedded context (shadow DOM). However, adding !important reverses the order:

  1. !important shadow context (most powerful)
  2. !important host context
  3. normal host context
  4. normal shadow context (least powerful)

Important styles that come from inside a shadow context override important styles defined by the host document. Here’s an odd-bird custom element with some styles written in the element template (shadow DOM), and some styles in the host page (light DOM) stylesheet:

CodePen Embed Fallback

Both color declarations have normal importance, and so the host page mediumvioletred takes priority. But the font-family declarations are flagged !important, giving advantage to the shadow-context, where fantasy is defined.

Important layers

Cascade layers work the same way as both origins and context, with the important layers in reverse-order. The only difference is that layers make that behavior much more noticeable.

Once we start using cascade layers, we will need to be much more cautious and intentional about how we use !important. It’s no longer a quick way to jump to the top of the priorities — but an integrated part of our cascade layering; a way for lower layers to insist that some of their styles are essential.

Since cascade layers are customizable, there’s no pre-defined order. But we can imagine starting with three layers:

  1. utilities (most powerful)
  2. components
  3. defaults (least powerful)

When styles in those layers are marked as important, they would generate three new, reversed important layers:

  1. !important defaults (most powerful)
  2. !important components
  3. !important utilities
  4. normal utilities
  5. normal components
  6. normal defaults (least powerful)

In this example, the color is defined by all three normal layers, and the utilities layer wins the conflict, applying the maroon color, as the utilities layer has a higher priority in @layers. But notice that the text-decoration property is marked !important in both the defaults and components layers, where important defaults take priority, applying the underline declared by defaults:

CodePen Embed Fallback Establishing a layer order

We can create any number of layers and name them or group them in various ways. But the most important thing to do is to make sure our layers are applied in the right order of priority.

A single layer can be used multiple times throughout the codebase — cascade layers stack in the order they first appear. The first layer encountered sits at the bottom (least powerful), and the last layer at the top (most powerful). But then, above that, un-layered styles have the highest priority:

@layer layer-1 { a { color: red; } } @layer layer-2 { a { color: orange; } } @layer layer-3 { a { color: yellow; } } /* un-layered */ a { color: green; }
  1. un-layered styles (most powerful)
  2. layer-3
  3. layer-2
  4. layer-1 (least powerful)

Then, as discussed above, any important styles are applied in a reverse order:

@layer layer-1 { a { color: red !important; } } @layer layer-2 { a { color: orange !important; } } @layer layer-3 { a { color: yellow !important; } } /* un-layered */ a { color: green !important; }
  1. !important layer-1 (most powerful)
  2. !important layer-2
  3. !important layer-3
  4. !important un-layered styles
  5. normal un-layered styles
  6. normal layer-3
  7. normal layer-2
  8. normal layer-1 (least powerful)

Layers can also be grouped, allowing us to do more complicated sorting of top-level and nested layers:

@layer layer-1 { a { color: red; } } @layer layer-2 { a { color: orange; } } @layer layer-3 { @layer sub-layer-1 { a { color: yellow; } } @layer sub-layer-2 { a { color: green; } } /* un-nested */ a { color: blue; } } /* un-layered */ a { color: indigo; }
  1. un-layered styles (most powerful)
  2. layer-3
    1. layer-3 un-nested
    2. layer-3 sub-layer-2
    3. layer-3 sub-layer-1
  3. layer-2
  4. layer-1 (least powerful)

Grouped layers always stay together in the final layer order (for example, sub-layers of layer-3 will all be next to each other), but this otherwise behaves the same as if the list was “flattened” — turning this into a single six-item list. When reversing !important layer order, the entire list flattened is reversed.

But layers don’t have to be defined once in a single location. We give them names so that layers can be defined in one place (to establish layer order), and then we can append styles to them from anywhere:

/* describe the layer in one place */ @layer my-layer; /* append styles to it from anywhere */ @layer my-layer { a { color: red; } }

We can even define a whole ordered list of layers in a single declaration:

@layer one, two, three, four, five, etc;

This makes it possible for the author of a site to have final say over the layer order. By providing a layer order up-front, before any third party code is imported, the order can be established and rearranged in one place without worrying about how layers are used in any third-party tool.

Syntax: Working with cascade layers

Let’s take a look at the syntax!

Order-setting @layer statements

Since layers are stacked in the order they are defined, it’s important that we have a tool for establishing that order all in one place!

We can use @layer statements to do that. The syntax is:

@layer <layer-name>#;

That hash (#) means we can add as many layer names as we want in a comma-separated list:

@layer reset, defaults, framework, components, utilities;

That will establish the layer order:

  1. un-layered styles (most powerful)
  2. utilities
  3. components
  4. framework
  5. defaults
  6. reset (least powerful)

We can do this as many times as we want, but remember: what matters is the order each name first appears. So this will have the same result:

@layer reset, defaults, framework; @layer components, defaults, framework, reset, utilities;

The ordering logic will ignore the order of reset, defaults, and framework in the second @layer rule because those layers have already been established. This @layer list syntax doesn’t add any special magic to the layer ordering logic: layers are stacked based on the order in which the layers first appear in your code. In this case, reset appears first in the first @layer list. Any @layer statement that comes later can only append layer names to the list, but can’t move layers that already exist. This ensures that you can always control the final overall layer order from one location — at the very start of your styles.

These layer-ordering statements are allowed at the top of a stylesheet, before the @import rule (but not between imports). We highly recommend using this feature to establish all your layers up-front in a single place so you always know where to look or make changes.

Block @layer rules

The block version of the @layer rule only takes a single layer name, but then allows you to add styles to that layer:

@layer <layer-name> { /* styles added to the layer */ }

You can put most things inside an @layer block — media queries, selectors and styles, support queries, etc. The only things you can’t put inside a layer block are things like charset, imports, and namespaces. But don’t worry, there is a syntax for importing styles into a layer.

If the layer name hasn’t been established before, this layer rule will add it to the layer order. But if the name has been established, this allows you to add styles to existing layers from anywhere in the document — without changing the priority of each layer.

If we’ve established our layer-order up-front with the layer statement rule, we no longer need to worry about the order of these layer blocks:

/* establish the order up-front */ @layer defaults, components, utilities; /* add styles to layers in any order */ @layer utilities { [hidden] { display: none; } } /* utilities will override defaults, based on established order */ @layer defaults { * { box-sizing: border-box; } img { display: block; } } Grouping (nested) layers

Layers can be grouped, by nesting layer rules:

@layer one { /* sorting the sub-layers */ @layer two, three; /* styles ... */ @layer three { /* styles ... */ } @layer two { /* styles ... */ } }

This generates grouped layers that can be represented by joining the parent and child names with a period. That means the resulting sub-layers can also be accessed directly from outside the group:

/* sorting nested layers directly */ @layer one.two, one.three; /* adding to nested layers directly */ @layer one.three { /* ... */ } @layer one.two { /* ... */ }

The rules of layer-ordering apply at each level of nesting. Any styles that are not further nested are considered “un-layered” in that context, and have priority over further nested styles:

@layer defaults { /* un-layered defaults (higher priority) */ :any-link { color: rebeccapurple; } /* layered defaults (lower priority) */ @layer reset { a[href] { color: blue; } } }

Grouped layers are also contained within their parent, so that the layer order does not intermix across groups. In this example, the top level layers are sorted first, and then the layers are sorted within each group:

@layer reset.type, default.type, reset.media, default.media;

Resulting in a layer order of:

  • un-layered (most powerful)
  • default group
    • default un-layered
    • default.media
    • default.type
  • reset group
    • reset un-layered
    • reset.media
    • reset.type

Note that layer names are also scoped so that they don’t interact or conflict with similarly-named layers outside their nested context. Both groups can have distinct media sub-layers.

This grouping becomes especially important when using @import or <link> to layer entire stylesheets. A third-party tool, like Bootstrap, could use layers internally — but we can nest those layers into a shared bootstrap layer-group on import, to avoid potential layer-naming conflicts.

Layering entire stylesheets with @import or <link>

Entire stylesheets can be added to a layer using the new layer() function syntax with @import rules:

/* styles imported into to the <layer-name> layer */ @import url('example.css') layer(<layer-name>);

There is also a proposal to add a layer attribute in the HTML <link> element — although this is still under development, and not yet supported anywhere. This can be used to import third-party tools or component libraries, while grouping any internal layers together under a single layer name — or as a way of organizing layers into distinct files.

Anonymous (un-named) layers

Layer names are helpful as they allow us to access the same layer from multiple places for sorting or combining layer blocks — but they are not required.

It’s possible to create anonymous (un-named) layers using the block layer rule:

@layer { /* ... */ } @layer { /* ... */ }

Or using the import syntax, with a layer keyword in place of the layer() function:

/* styles imported into to a new anonymous layer */ @import url('../example.css') layer;

Each anonymous layer is unique, and added to the layer order where it is encountered. Anonymous layers can’t be referenced from other layer rules for sorting or appending more styles.

These should probably be used sparingly, but there might be a few use cases:

  • Projects could ensure that all styles for a given layer are required to be located in a single place.
  • Third-party tools could “hide” their internal layering inside anonymous layers so that they don’t become part of the tool’s public API.
Reverting values to the previous layer

There are several ways that we can use to “revert” a style in the cascade to a previous value, defined by a lower priority origin or layer. That includes a number of existing global CSS values, and a new revert-layer keyword that will also be global (works on any property).

Context: Existing global cascade keywords*

CSS has several global keywords which can be used on any property to help roll-back the cascade in various ways.

  • initial sets a property to the specified value before any styles (including browser defaults) are applied. This can be surprising as we often think of browser styles as the initial value — but, for example, the initial value of display is inline, no matter what element we use it on.
  • inherit sets the property to apply a value from its parent element. This is the default for inherited properties, but can still be used to remove a previous value.
  • unset acts as though simply removing all previous values — so that inherited properties once again inherit, while non-inherited properties return to their initial value.
  • revert only removes values that we’ve applied in the author origin (i.e. the site styles). This is what we want in most cases, since it allows the browser and user styles to remain intact.
New: the revert-layer keyword

Cascade layers add a new global revert-layer keyword. It works the same as revert, but only removes values that we’ve applied in the current cascade layer. We can use that to roll back the cascade, and use whatever value was defined in the previous layers.

In this example, the no-theme class removes any values set in the theme layer.

@layer default { a { color: maroon; } } @layer theme { a { color: var(--brand-primary, purple); } .no-theme { color: revert-layer; } }

So a link tag with the .no-theme class will roll back to use the value set in the default layer. When revert-layer is used in un-layered styles, it behaves the same as revert — rolling back to the previous origin.

Reverting important layers

Things get interesting if we add !important to the revert-layer keyword. Because each layer has two distinct “normal” and “important” positions in the cascade, this doesn’t simply change the priority of the declaration — it changes what layers are reverted.

Let’s assume we have three layers defined, in a layer stack that looks like this:

  1. utilities (most powerful)
  2. components
  3. defaults (least powerful)

We can flesh that out to include not just normal and important positions of each layer, but also un-layered styles, and animations:

  1. !important defaults (most powerful)
  2. !important components
  3. !important utilities
  4. !important un-layered styles
  5. CSS animations
  6. normal un-layered styles
  7. normal utilities
  8. normal components
  9. normal defaults (least powerful)

Now, when we use revert-layer in a normal layer (let’s use utilities) the result is fairly direct. We revert only that layer, while everything else applies normally:

  1. ✅ !important defaults (most powerful)
  2. ✅ !important components
  3. ✅ !important utilities
  4. ✅ !important un-layered styles
  5. ✅ CSS animations
  6. ✅ normal un-layered styles
  7. ❌ normal utilities
  8. ✅ normal components
  9. ✅ normal defaults (least powerful)

But when we move that revert-layer into the important position, we revert both the normal and important versions along with everything in-between:

  1. ✅ !important defaults (most powerful)
  2. ✅ !important components
  3. ❌ !important utilities
  4. ❌ !important un-layered styles
  5. ❌ CSS animations
  6. ❌ normal un-layered styles
  7. ❌ normal utilities
  8. ✅ normal components
  9. ✅ normal defaults (least powerful)
Use cases: When would I want to use cascade layers?

So what sort of situations might we find ourselves using cascade layers? Here are several examples of when cascade layers make a lot of sense, as well as others where they do not make a lot sense.

Less intrusive resets and defaults

One of the clearest initial use cases would be to make low-priority defaults that are easy to override.

Some resets have been doing this already by applying the :where() pseudo-class around each selector. :where() removes all specificity from the selectors it is applied to, which has the basic impact desired, but also some downsides:

  • It has to be applied to each selector individually
  • Conflicts inside the reset have to be resolved without specificity

Layers allow us to more simply wrap the entire reset stylesheet, either using the block @layer rule:

/* reset.css */ @layer reset { /* all reset styles in here */ }

Or when you import the reset:

/* reset.css */ @import url(reset.css) layer(reset);

Or both! Layers can be nested without changing their priority. This way, you can use a third-party reset, and ensure it gets added to the layer you want whether or not the reset stylesheet itself is written using layers internally.

Since layered styles have a lower priority than default “un-layered” styles, this is a good way to start using cascade layers without re-writing your entire CSS codebase.

The reset selectors still have specificity information to help resolve internal conflicts, without wrapping each individual selector — but you also get the desired outcome of a reset stylesheet that is easy to override.

Managing a complex CSS architecture

As projects become larger and more complex, it can be useful to define clearer boundaries for naming and organizing CSS code. But the more CSS we have, the more potential we have for conflicts — especially from different parts of a system like a “theme” or a “component library” or a set of “utility classes.”

Not only do we want these organized by function, but it can also be useful to organize them based on what parts of the system take priority in the case of a conflict. Harry Robert’s Inverted Triangle CSS does a good job visualizing what those layers might contain.

In fact, the initial pitch for adding layers to the CSS cascade used the ITCSS methodology as a primary example, and a guide for developing the feature.

There is no particular technique required for this, but it’s likely helpful to restrict projects to a pre-defined set of top-level layers and then extend that set with nested layers as appropriate.

For example:

  1. low level reset and normalization styles
  2. element defaults, for basic typography and legibility
  3. themes, like light and dark modes
  4. re-usable patterns that might appear across multiple components
  5. layouts and larger page structures
  6. individual components
  7. overrides and utilities

We can create that top-level layer stack at the very start of our CSS, with a single layer statement:

@layer reset, default, themes, patterns, layouts, components, utilities;

The exact layers needed, and how you name those layers, might change from one project to the next.

From there, we create even more detailed layer breakdowns. Maybe our components themselves have defaults, structures, themes, and utilities internally.

@layer components { @layer defaults, structures, themes, utilities; }

Without changing the top-level structure, we now have a way to further layer the styles within each component.

Using third-party tools and frameworks

Integrating third-party CSS with a project is one of the most common places to run into cascade issues. Whether we’re using a shared reset like Normalizer or CSS Remedy, a generic design system like Material Design, a framework like Bootstrap, or a utility toolkit like Tailwind — we can’t always control the selector specificity or importance of all the CSS being used on our sites. Sometimes, this even extends to internal libraries, design systems, and tools managed elsewhere in an organization.

As a result, we often have to structure our internal CSS around the third-party code, or escalate conflicts when they come up — with artificially high specificity or !important flags. And then we have to maintain those hacks over time, adapting to upstream changes.

Cascade layers give us a way to slot third-party code into the cascade of any project exactly where we want it to live — no matter how selectors are written internally. Depending on the type of library we’re using, we might do that in various ways. Let’s start with a basic layer-stack, working our way up from resets to utilities:

@layer reset, type, theme, components, utilities;

And then we can incorporate some tools…

Using a reset

If we’re using a tool like CSS Remedy, we might also have some reset styles of our own that we want to include. Let’s import CSS Remedy into a sub-layer of reset:

@import url('remedy.css') layer(reset.remedy);

Now we can add our own reset styles to the reset layer, without any further nesting (unless we want it). Since styles directly in reset will override any further nested styles, we can be sure our styles will always take priority over CSS Remedy if there’s a conflict — no matter what changes in a new release:

@import url('remedy.css') layer(reset.remedy); @layer reset { :is(ol, ul)[role='list'] { list-style: none; padding-inline-start: 0; } }

And since the reset layer is at the bottom of the stack, the rest of the CSS in our system will override both Remedy, and our own local reset additions.

Using utility classes

At the other end of our stack, “utility classes” in CSS can be a useful way to reproduce common patterns (like additional context for screen readers) in a broadly-applicable way. Utilities tend to break the specificity heuristic, since we want them defined broadly (resulting in a low specificity), but we also generally want them to “win” conflicts.

By having a utilities layer at the top of our layer stack, we can make that possible. We can use that in a similar way to the reset example, both loading external utilities into a sub-layer, and providing our own:

@import url('tailwind.css') layer(utilities.tailwind); @layer utilities { /* from https://kittygiraudel.com/snippets/sr-only-class/ */ /* but with !important removed from the properties */ .sr-only { border: 0; clip: rect(1px, 1px, 1px, 1px); -webkit-clip-path: inset(50%); clip-path: inset(50%); height: 1px; overflow: hidden; margin: -1px; padding: 0; position: absolute; width: 1px; white-space: nowrap; } } Using design systems and component libraries

There are a lot of CSS tools that fall somewhere in the middle of our layer stack — combining typography defaults, themes, components, and other aspects of a system.

Depending on the particular tool, we might do something similar to the reset and utility examples above — but there are a few other options. A highly integrated tool might deserve a top-level layer:

@layer reset, bootstrap, utilities; @import url('bootstrap.css') layer(bootstrap);

If these tools start to provide layers as part of their public API, we could also break it down into parts — allowing us to intersperse our code with the library:

@import url('bootstrap/reset.css') layer(reset.bootstrap); @import url('bootstrap/theme.css') layer(theme.bootstrap); @import url('bootstrap/components.css') layer(components.bootstrap); @layer theme.local { /* styles here will override theme.bootstrap */ /* but not interfere with styles from components.bootstrap */ } Using layers with existing (un-layered, !important-filled) frameworks

As with any major language change, there’s going to be an adjustment period when CSS Cascade Layers become widely adopted. What happens if your team is ready to start using layers next month, but your favorite framework decides to wait another three years before they switch over to layered styles? Many frameworks will likely still use !important more often than we’d like! With !important layers reversed, that’s not ideal.

Still, layers can still help us solve the problem. We just have to get clever about it. We decide what layers we want for our project, and that means we can add layers above and also below the framework layers we create.

For now, though, we can use a lower layer to override !important styles from the framework, and a higher layer to override normal styles. Something like this:

@layer framework.important, framework.bootstrap, framework.local; @import url('bootstrap.css') layer(framework.bootstrap); @layer framework.local { /* most of our normal framework overrides can live here */ } @layer framework.important { /* add !important styles in a lower layer */ /* to override any !important framework styles */ }

It still feels like a bit of a hack, but it helps move us in the right direction — towards a more structured cascade. Hopefully it’s a temporary fix.

Designing a CSS tool or framework

For anyone maintaining a CSS library, cascade layers can help with internal organization, and even become part of the developer API. By naming internal layers of a library, we can allow users of our framework to hook into those layers when customizing or overriding our provided styles.

For example, Bootstrap could expose layers for their “reboot,” “grid,” and “utilities” — likely stacked in that order. Now a user can decide if they want to load those Bootstrap layers into different local layers:

@import url(bootstrap/reboot.css) layer(reset); /* reboot » reset.reboot */ @import url(bootstrap/grid.css) layer(layout); /* grid » layout.grid */ @import url(bootstrap/utils.css) layer(override); /* utils » override.utils */

Or the user might load them into a Bootstrap layer, with local layers interspersed:

@layer bs.reboot, bs.grid, bs.grid-overrides, bs.utils, bs.util-overrides; @import url('bootstrap-all.css') layer(bs);

It’s also possible to hide internal layering from users, when desired, by grouping any private/internal layers inside an anonymous (un-named) layer. Anonymous layers will get added to the layer order where they are encountered, but will not be exposed to users re-arranging or appending styles.

I just want this one property to be more !important

Counter to some expectations, layers don’t make it easy to quickly escalate a particular style so that it overrides another.

If the majority of our styles are un-layered, then any new layer will be de-prioritized in relation to the default. We could do that to individual style blocks, but it would quickly become difficult to track.

Layers are intended to be more foundational, not style-by-style, but establishing consistent patterns across a project. Ideally, if we’ve set that up right, we get the correct result by moving our style to the appropriate (and pre-defined) layer.

If the majority of our styles already fall into well-defined layers, we can always consider adding a new highest-power layer at the top of a given stack, or using un-layered styles to override the layers. We might even consider having a debug layer at the top of the stack, for doing exploratory work outside of production.

But adding new layers on-the-fly can defeat the organizational utility of this feature, and should be used carefully. It’s best to ask: Why should this style override the other?

If the answer has to do with one type of style always overriding another type, layers are probably the right solution. That might be because we’re overriding styles that come from a place we don’t control, or because we’re writing a utility, and it should move into our utilities layer. If the answer has to do with more targeted styles overriding less targeted styles, we might consider making the selectors reflect that specificity.

Or, on rare occasions, we might even have styles that really are important — the feature simply doesn’t work if you override this particular style. We might say adding display: none to the [hidden] attribute belongs in our lowest-priority reset, but should still be hard to override. In that case, !important really is the right tool for the job:

@layer reset { [hidden] { display: none !important; } } Scoping and name-spacing styles? Nope!

Cascade layers are clearly an organizational tool, and one that ‘captures’ the impact of selectors, especially when they conflict. So it can be tempting at first glance to see them as a solution for managing scope or name-spacing.

A common first-instinct is to create a layer for each component in a project — hoping that will ensure (for example) that .post-title is only applied inside a .post.

But cascade conflicts are not the same as naming conflicts, and layers aren’t particularly well designed for this type of scoped organization. Cascade layers don’t constrain how selectors match or apply to the HTML, only how they cascade together. So unless we can be sure that component X always override component Y, individual component layers won’t help much. Instead, we’ll need to keep an eye on the proposed @scope spec that is being developed.

It can be useful to think of layers and component-scopes instead as overlapping concerns:

Scopes describe what we are styling, while layers describe why we are styling. We can also think of layers as representing where the style comes from, while scopes represent what the style will attach to.

Test your knowledge: Which style wins?

For each situation, assume this paragraph:

<p id="intro">Hello, World!</p> Question 1 @layer ultra-high-priority { #intro { color: red; } } p { color: green; } What color is the paragraph?

Despite the layer having a name that sounds pretty important, un-layered styles have a higher priority in the cascade. So the paragraph will be green.

Question 2 @layer ren, stimpy; @layer ren { p { color: red !important; } } p { color: green; } @layer stimpy { p { color: blue !important; } } What color is the paragraph?

Our normal layer order is established at the start — ren at the bottom, then stimpy, then (as always) un-layered styles at the top. But these styles aren’t all normal, some of them are important. Right away, we can filter down to just the !important styles, and ignore the unimportant green. Remember that ‘origins and importance’ are the first step of the cascade, before we even take layering into account.

That leaves us with two important styles, both in layers. Since our important layers are reversed, ren moves to the top, and stimpy to the bottom. The paragraph will be red.

Question 3 @layer Montagues, Capulets, Verona; @layer Montagues.Romeo { #intro { color: red; } } @layer Montagues.Benvolio { p { color: orange; } } @layer Capulets.Juliet { p { color: yellow; } } @layer Verona { * { color: blue; } } @layer Capulets.Tybalt { #intro { color: green; } } What color is the paragraph?

All our styles are in the same origin and context, none are marked as important, and none of them are inline styles. We do have a broad range of selectors here, from a highly specific ID #intro to a zero specificity universal * selector. But layers are resolved before we take specificity into account, so we can ignore the selectors for now.

The primary layer order is established up front, and then sub-layers are added internally. But sub-layers are sorted along with their parent layer — meaning all the Montagues will have lowest priority, followed by all the Capulets, and then Verona has final say in the layer order. So we can immediately filter down to just the Verona styles, which take precedence. Even though the * selector has zero specificity, it will win.

Be careful about putting universal selectors in powerful layers!

Debugging layer conflicts in browser developer tools

Chrome, Safari, Firefox, and Edge browsers all have developer tools that allow you to inspect the styles being applied to a given element on the page. The styles panel of this element inspector will show applied selectors, sorted by their cascade priority (highest priority at the top), and then inherited styles below. Styles that are not being applied for any reason will generally be grayed out, or even crossed out — sometimes with additional information about why the style is not applied. This is the first place to look when debugging any aspect of the cascade, including layer conflicts.

Safari Technology Preview and Firefox Nightly already show (and sort) cascade layers in this panel. This tooling is expected to role out in the stable versions at the same time as cascade layers. The layer of each selector is listed directly above the selector itself:

Safari Firefox

Chrome/Edge are working on similar tools and expect to have them available in Canary (nightly) releases by the time cascade layers land in the stable release. We’ll make updates here as those tools change and improve.

Browser support and fallbacks

Cascade layers are (or will soon be) available by default in all the three major browser engines:

  • Chrome/Edge 99+
  • Firefox 97+
  • Safari (currently in the Technology Preview)

Since layers are intended as foundational building blocks of an entire CSS architecture, it is difficult to imagine building manual fallbacks in the same way you might for other CSS features. The fallbacks would likely involve duplicating large sections of code, with different selectors to manage cascade layering — or providing a much simpler fallback stylesheet.

Query feature support using @supports

There is a @supports feature in CSS that will allow authors to test for support of @layer and other at-rules:

@supports at-rule(@layer) { /* code applied for browsers with layer support */ } @supports not at-rule(@layer) { /* fallback applied for browsers without layer support */ }

However, it’s also not clear when this query itself will be supported in browsers.

Assigning layers in HTML with the <link> tag

There is no official specification yet for a syntax to layer entire stylesheets from the html <link> tag, but there is a proposal being developed. That proposal includes a new layer attribute which can be used to assign the styles to a named or anonymous layer:

<!-- styles imported into to the <layer-name> layer --> <link rel="stylesheet" href="example.css" layer="<layer-name>"> <!-- styles imported into to a new anonymous layer --> <link rel="stylesheet" href="example.css" layer>

However, old browsers without support for the layer attribute will ignore it completely, and continue to load the stylesheet without any layering. The results could be pretty unexpected. So the proposal also extends the existing media attribute, so that it allows feature support queries in a support() function.

That would allow us to make layered links conditional, based on support for layering:

<link rel="stylesheet" layer="bootstrap" media="supports(at-rule(@layer))" href="bootstrap.css"> Potential polyfills and workarounds

The major browsers have all moved to an “evergreen” model with updates pushed to users on a fairly short release cycle. Even Safari regularly releases new features in “patch” updates between their more rare-seeming major versions.

That means we can expect browser support for these features to ramp up very quickly. For many of us, it may be reasonable to start using layers in only a few months, without much concern for old browsers.

For others, it may take longer to feel comfortable with the native browser support. There are many other ways to manage the cascade, using selectors, custom properties, and other tools. It’s also theoretically possible to mimic (or polyfill) the basic behavior. There are people working on that polyfill, but it’s not clear when that will be ready either.

More resources

CSS Cascade Layers is still evolving but there is already a lot of resources, including documentation, articles, videos, and demos to help you get even more familiar with layers and how they work.

Reference Articles Videos Demos

A Complete Guide to CSS Cascade Layers originally published on CSS-Tricks. You should get the newsletter.

Comparing Node JavaScript to JavaScript in the Browser

Css Tricks - Fri, 02/18/2022 - 9:01am

Being able to understand Node continues to be an important skill if you’re a front-end developer. Deno has arrived as another way to run JavaScript outside the browser, but the huge ecosystem of tools and software built with Node mean it’s not going anywhere anytime soon.

If you’ve mainly written JavaScript that runs in the browser and you’re looking to get more of an understanding of the server side, many articles will tell you that Node JavaScript is a great way to write server-side code and capitalize on your JavaScript experience.

I agree, but there are a lot of challenges jumping into Node.js, even if you’re experienced at authoring client-side JavaScript. This article assumes you’ve got Node installed, and you’ve used it to build front-end apps, but want to write your own APIs and tools using Node.

For a beginners explanation of Node and npm you can check out Jamie Corkhill’s “Getting Started With Node” on Smashing Magazine.

Asynchronous JavaScript

We don’t need to write a whole lot of asynchronous code on the browser. The most common usage of asynchronous code on the browser is fetching data from an API using fetch (or XMLHttpRequest if you’re old-school). Other uses of async code might include using setInterval, setTimeout, or responding to user input events, but we can get pretty far writing JavaScript UI without being asynchronous JavaScript geniuses.

If you’re using Node, you will nearly always be writing asynchronous code. From the beginning, Node has been built to leverage a single-threaded event loop using asynchronous callbacks. The Node team blogged in 2011 about how “Node.js promotes an asynchronous coding style from the ground up.” In Ryan Dahl’s talk announcing Node.js in 2009, he talks about the performance benefits of doubling down on asynchronous JavaScript.

The asynchronous-first style is part of the reason Node gained popularity over other attempts at server-side JavaScript implementations such as Netscape’s application servers or Narwhal. However, being forced to write asynchronous JavaScript might cause friction if you aren’t ready for it.

Setting up an example

Let’s say we’re writing a quiz app. We’re going to allow users to build quizes out of multichoice questions to test their friends’ knowledge. You can find a more complete version of what we’ll build at this GitHub repo. You could also clone the entire front-end and back-end to see how it all fits together, or you can take a look at this CodeSandbox (run npm run start to fire it up) and get an idea of what we’re making from there.

The quizzes in our app will consist of a bunch of questions, and each of these questions will have a number of answers to choose from, with only one answer being correct.

We can hold this data in an SQLite database. Our database will contain:

  • A table for quizzes with two columns:
    • an integer ID
    • a text title
  • A table for questions with three columns:
    • an integer ID
    • body text
    • An integer reference matching the ID of the quiz each question belongs to
  • A table for answers with four columns:
    • an integer ID
    • body text
    • whether the answer is correct or not
    • an integer reference matching the ID of the question each answer belongs to

SQLite doesn’t have a boolean data type, so we can hold whether an answer is correct in an integer where 0 is false and 1 is true.

First, we’ll need to initialize npm and install the sqlite3 npm package from the command line:

npm init -y npm install sqlite3

This will create a package.json file. Let’s edit it and add:


To the top-level JSON object. This will allow us to use modern ES6 module syntax. Now we can create a node script to set up our tables. Let’s call our script migrate.js.

// migrate.js import sqlite3 from "sqlite3"; let db = new sqlite3.Database("quiz.db"); db.serialize(function () { // Setting up our tables: db.run("CREATE TABLE quiz (quizid INTEGER PRIMARY KEY, title TEXT)"); db.run("CREATE TABLE question (questionid INTEGER PRIMARY KEY, body TEXT, questionquiz INTEGER, FOREIGN KEY(questionquiz) REFERENCES quiz(quizid))"); db.run("CREATE TABLE answer (answerid INTEGER PRIMARY KEY, body TEXT, iscorrect INTEGER, answerquestion INTEGER, FOREIGN KEY(answerquestion) REFERENCES question(questionid))"); // Create a quiz with an id of 0 and a title "my quiz" db.run("INSERT INTO quiz VALUES(0,\"my quiz\")"); // Create a question with an id of 0, a question body // and a link to the quiz using the id 0 db.run("INSERT INTO question VALUES(0,\"What is the capital of France?\", 0)"); // Create four answers with unique ids, answer bodies, an integer for whether // they're correct or not, and a link to the first question using the id 0 db.run("INSERT INTO answer VALUES(0,\"Madrid\",0, 0)"); db.run("INSERT INTO answer VALUES(1,\"Paris\",1, 0)"); db.run("INSERT INTO answer VALUES(2,\"London\",0, 0)"); db.run("INSERT INTO answer VALUES(3,\"Amsterdam\",0, 0)"); }); db.close();

I’m not going to explain this code in detail, but it creates the tables we need to hold our data. It will also create a quiz, a question, and four answers, and store all of this in a file called quiz.db. After saving this file, we can run our script from the command line using this command:

node migrate.js

If you like, you can open the database file using a tool like DB Browser for SQLite to double check that the data has been created.

Changing the way you write JavaScript

Let’s write some code to query the data we’ve created.

Create a new file and call it index.js .To access our database, we can import sqlite3, create a new sqlite3.Database, and pass the database file path as an argument. On this database object, we can call the get function, passing in an SQL string to select our quiz and a callback that will log the result:

// index.js import sqlite3 from "sqlite3"; let db = new sqlite3.Database("quiz.db"); db.get(`SELECT * FROM quiz WHERE quizid = 0`, (err, row) => { if (err) { console.error(err.message); } console.log(row); db.close(); });

Running this should print { quizid: 0, title: 'my quiz' } in the console.

How not to use callbacks

Now let’s wrap this code in a function where we can pass the ID in as an argument; we want to access any quiz by its ID. This function will return the database row object we get from db.

Here’s where we start running into trouble. We can’t simply return the object inside of the callback we pass to db and walk away. This won’t change what our outer function returns. Instead, you might think we can create a variable (let’s call it result) in the outer function and reassign this variable in the callback. Here is how we might attempt this:

// index.js // Be warned! This code contains BUGS import sqlite3 from "sqlite3"; function getQuiz(id) { let db = new sqlite3.Database("quiz.db"); let result; db.get(`SELECT * FROM quiz WHERE quizid = ?`, [id], (err, row) => { if (err) { return console.error(err.message); } db.close(); result = row; }); return result; } console.log(getQuiz(0));

If you run this code, the console log will print out undefined! What happened?

We’ve run into a disconnect between how we expect JavaScript to run (top to bottom), and how asynchronous callbacks run. The getQuiz function in the above example runs like this:

  1. We declare the result variable with let result;. We haven’t assigned anything to this variable so its value is undefined.
  2. We call the db.get() function. We pass it an SQL string, the ID, and a callback. But our callback won’t run yet! Instead, the SQLite package starts a task in the background to read from the quiz.db file. Reading from the file system takes a relatively long time, so this API lets our user code move to the next line while Node.js reads from the disk in the background.
  3. Our function returns result. As our callback hasn’t run yet, result still holds a value of undefined.
  4. SQLite finishes reading from the file system and runs the callback we passed, closing the database and assigning the row to the result variable. Assigning this variable makes no difference as the function has already returned its result.
Passing in callbacks

How do we fix this? Before 2015, the way to fix this would be to use callbacks. Instead of only passing the quiz ID to our function, we pass the quiz ID and a callback which will receive the row object as an argument.

Here’s how this looks:

// index.js import sqlite3 from "sqlite3"; function getQuiz(id, callback) { let db = new sqlite3.Database("quiz.db"); db.get(`SELECT * FROM quiz WHERE quizid = ?`, [id], (err, row) => { if (err) { console.error(err.message); } else { callback(row); } db.close(); }); } getQuiz(0,(quiz)=>{ console.log(quiz); });

That does it. It’s a subtle difference, and one that forces you to change the way your user code looks, but it means now our console.log runs after the query is complete.

Callback hell

But what if we need to do multiple consecutive asynchronous calls? For instance, what if we were trying to find out which quiz an answer belonged to, and we only had the ID of the answer.

First, I’m going to refactor getQuiz to a more general get function, so we can pass in the table and column to query, as well as the ID:

Unfortunately, we are unable to use the (more secure) SQL parameters for parameterizing the table name, so we’re going to switch to using a template string instead. In production code you would need to scrub this string to prevent SQL injection.

function get(params, callback) { // In production these strings should be scrubbed to prevent SQL injection const { table, column, value } = params; let db = new sqlite3.Database("quiz.db"); db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => { callback(err, row); db.close(); }); }

Another issue is that there might be an error reading from the database. Our user code will need to know whether each database query has had an error; otherwise it shouldn’t continue querying the data. We’ll use the Node.js convention of passing an error object as the first argument of our callback. Then we can check if there’s an error before moving forward.

Let’s take our answer with an id of 2 and check which quiz it belongs to. Here’s how we can do this with callbacks:

// index.js import sqlite3 from "sqlite3"; function get(params, callback) { // In production these strings should be scrubbed to prevent SQL injection const { table, column, value } = params; let db = new sqlite3.Database("quiz.db"); db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => { callback(err, row); db.close(); }); } get({ table: "answer", column: "answerid", value: 2 }, (err, answer) => { if (err) { console.log(err); } else { get( { table: "question", column: "questionid", value: answer.answerquestion }, (err, question) => { if (err) { console.log(err); } else { get( { table: "quiz", column: "quizid", value: question.questionquiz }, (err, quiz) => { if (err) { console.log(err); } else { // This is the quiz our answer belongs to console.log(quiz); } } ); } } ); } });

Woah, that’s a lot of nesting! Every time we get an answer back from the database, we have to add two layers of nesting — one to check for an error, and one for the next callback. As we chain more and more asynchronous calls our code gets deeper and deeper.

We could partially prevent this by using named functions instead of anonymous functions, which would keep the nesting lower, but make our code our code less concise. We’d also have to think of names for all of these intermediate functions. Thankfully, promises arrived in Node back in 2015 to help with chained asynchronous calls like this.


Wrapping asynchronous tasks with promises allows you to prevent a lot of the nesting in the previous example. Rather than having deeper and deeper nested callbacks, we can pass a callback to a Promise’s then function.

First, let’s change our get function so it wraps the database query with a Promise:

// index.js import sqlite3 from "sqlite3"; function get(params) { // In production these strings should be scrubbed to prevent SQL injection const { table, column, value } = params; let db = new sqlite3.Database("quiz.db"); return new Promise(function (resolve, reject) { db.get(`SELECT * FROM ${table} WHERE ${column} = ${value}`, (err, row) => { if (err) { return reject(err); } db.close(); resolve(row); }); }); }

Now our code to search for which quiz an answer is a part of can look like this:

get({ table: "answer", column: "answerid", value: 2 }) .then((answer) => { return get({ table: "question", column: "questionid", value: answer.answerquestion, }); }) .then((question) => { return get({ table: "quiz", column: "quizid", value: question.questionquiz, }); }) .then((quiz) => { console.log(quiz); }) .catch((error) => { console.log(error); } );

That’s a much nicer way to handle our asynchronous code. And we no longer have to individually handle errors for each call, but can use the catch function to handle any errors that happen in our chain of functions.

We still need to write a lot of callbacks to get this working. Thankfully, there’s a newer API to help! When Node 7.6.0 was released, it updated its JavaScript engine to V8 5.5 which includes the ability to write ES2017 async/await functions.


With async/await we can write our asynchronouse code almost the same way we write synchronous code. Sarah Drasner has a great post explaining async/await.

When you have a function that returns a Promise, you can use the await keyword before calling it, and it will prevent your code from moving to the next line until the Promise is resolved. As we’ve already refactored the get() function to return a promise, we only need to change our user-code:

async function printQuizFromAnswer() { const answer = await get({ table: "answer", column: "answerid", value: 2 }); const question = await get({ table: "question", column: "questionid", value: answer.answerquestion, }); const quiz = await get({ table: "quiz", column: "quizid", value: question.questionquiz, }); console.log(quiz); } printQuizFromAnswer();

This looks much more familiar to code that we’re used to reading. Just this year, Node released top-level await. This means we can make this example even more concise by removing the printQuizFromAnswer() function wrapping our get() function calls.

Now we have concise code that will sequentially perform each of these asynchronous tasks. We would also be able to simultaneously fire off other asynchronous functions (like reading from files, or responding to HTTP requests) while we’re waiting for this code to run. This is the benefit of all the asynchronous style.

As there are so many asynchronous tasks in Node, such as reading from the network or accessing a database or filesystem. It’s especially important to understand these concepts. It also has a bit of a learning curve.

Using SQL to its full potential

There’s an even better way! Instead of having to worry about these asynchronous calls to get each piece of data, we could use SQL to grab all the data we need in one big query. We can do this with an SQL JOIN query:

// index.js import sqlite3 from "sqlite3"; function quizFromAnswer(answerid, callback) { let db = new sqlite3.Database("quiz.db"); db.get( `SELECT *,a.body AS answerbody, ques.body AS questionbody FROM answer a INNER JOIN question ques ON a.answerquestion=ques.questionid INNER JOIN quiz quiz ON ques.questionquiz = quiz.quizid WHERE a.answerid = ?;`, [answerid], (err, row) => { if (err) { console.log(err); } callback(err, row); db.close(); } ); } quizFromAnswer(2, (e, r) => { console.log(r); });

This will return us all the data we need about our answer, question, and quiz in one big object. We’ve also renamed each body column for answers and questions to answerbody and questionbody to differentiate them. As you can see, dropping more logic into the database layer can simplify your JavaScript (as well as possibly improve performance).

If you’re using a relational database like SQLite, then you have a whole other language to learn, with a whole lot of different features that could save time and effort and increase performance. This adds more to the pile of things to learn for writing Node.

Node APIs and conventions

There are a lot of new node APIs to learn when switching from browser code to Node.js.

Any database connections and/or reads of the filesystem use APIs that we don’t have in the browser (yet). We also have new APIs to set up HTTP servers. We can make checks on the operating system using the OS module, and we can encrypt data with the Crypto module. Also, to make an HTTP request from node (something we do in the browser all the time), we don’t have a fetch or XMLHttpRequest function. Instead, we need to import the https module. However, a recent pull request in the node.js repository shows that fetch in node appears to be on the way! There are still many mismatches between browser and Node APIs. This is one of the problems that Deno has set out to solve.

We also need to know about Node conventions, including the package.json file. Most front-end developers will be pretty familiar with this if they’ve used build tools. If you’re looking to publish a library, the part you might not be used to is the main property in the package.json file. This property contains a path that will point to the entry-point of the library.

There are also conventions like error-first callbacks: where a Node API will take a callback which takes an error as the first argument and the result as the second argument. You could see this earlier in our database code and below using the readFile function.

import fs from 'fs'; fs.readFile('myfile.txt', 'utf8' , (err, data) => { if (err) { console.error(err) return } console.log(data) }) Different types of modules

Earlier on, I casually instructed you to throw "type":"module" in your package.json file to get the code samples working. When Node was created in 2009, the creators needed a module system, but none existed in the JavaScript specification. They came up with Common.js modules to solve this problem. In 2015, a module spec was introduced to JavaScript, causing Node.js to have a module system that was different from native JavaScript modules. After a herculean effort from the Node team we are now able to use these native JavaScript modules in Node.

Unfortunately, this means a lot of blog posts and resources will be written using the older module system. It also means that many npm packages won’t use native JavaScript modules, and sometimes there will be libraries that use native JavaScript modules in incompatible ways!

Other concerns

There are a few other concerns we need to think about when writing Node. If you’re running a Node server and there is a fatal exception, the server will terminate and will stop responding to any requests. This means if you make a mistake that’s bad enough on a Node server, your app is broken for everyone. This is different from client-side JavaScript where an edge-case that causes a fatal bug is experienced by one user at a time, and that user has the option of refreshing the page.

Security is something we should already be worried about in the front end with cross-site scripting and cross-site request forgery. But a back-end server has a wider surface area for attacks with vulnerabilities including brute force attacks and SQL injection. If you’re storing and accessing people’s information with Node you’ve got a big responsibility to keep their data safe.


Node is a great way to use your JavaScript skills to build servers and command line tools. JavaScript is a user-friendly language we’re used to writing. And Node’s async-first nature means you can smash through concurrent tasks quickly. But there are a lot of new things to learn when getting started. Here are the resources I wish I saw before jumping in:

And if you are planning to hold data in an SQL database, read up on SQL Basics.

Comparing Node JavaScript to JavaScript in the Browser originally published on CSS-Tricks. You should get the newsletter.

(Jay Freestone’s) Front-End Predictions for 2022

Css Tricks - Fri, 02/18/2022 - 5:34am

I linked to Jay’s front-end predictions last year and I think they panned out pretty well. I think he’s got a bit of a knack for that wide-scope look at front-end, including tooling and architecture, as well as browser tech. Let’s review.

Jay’s front-end predictions for 2021
  • &#x1f44d; React framework maturity is here in the fact that big players like Next and Gatsby are stable and have found pretty big markets. New players like Remix are arriving to clear success. Frameworks like Astro support React because it means picking up users who have less to learn.
  • &#x1f44d; Early container queries are here for sure. The syntax has nestled in a little bit, there are multiple browsers playing, and a polyfill is available. We’re even seeing early days of container units.
  • &#x1f44d; WASM adoption is more iffy to me. I don’t run in the circles that would be early adopters of it, but anecdotally, I probably heard it talked about more this year than any year past. That tells me it’s still of high interest.
  • &#x1f44d; Monoliths, if we’re just talking site building tools that “do it all” probably never stopped being popular, especially if you consider that’s what WordPress is and has only grown in popularity and usage. Perhaps Jamstack usage is growing, too, which decouples a lot of services. But the biggest players in helping you build Jamstack architectures also help you bring all those services under one roof. Services like Netlify and Vercel are Jamstack hosts, but will also run your serverless functions. Netlify Graph is a clear stab at helping you work with APIs in an in-house way, not to mention other features like form processing and auth that help make the approach feel more monolith-y. DigitalOcean’s App Platform is a Jamstack host, but designed such that any other additional services are right there. The same is true for AWS Amplify or Azure Static Web Apps.

I added to Jay’s list the inert attribute, which certainly didn’t get widely shipped, but we are seeing a smidge of progress on it, with Safari being the first-mover.

Jay’s front-end predictions for 2022 Source: jayfreestone.com

Let’s break them down:

  • Micro-frontends. I’m on the fence here, as I literally don’t get it. I think I’ll need to read more about it and see clear examples before I can weigh in on the technical choice here. But from a people perspective, it seems like a questionable move right out of the gate. I get that it’s complicated to get teams to work together perfectly, but splitting up their responsibilities and depth of technical knowledge doesn’t seem like a healthy solution that builds great sites.
  • The death of Jamstack (but not really). It’s mostly about the term itself, which I agree has gotten far too loaded and, thus, is losing meaning. “I think in 2022 we’ll see the confusion and exhaustion culminate in a gradual retreat from the term.” I can see that. Even using it in this post doesn’t feel quite right anymore.
  • Progressive enhancement. Jay points to frameworks producing functional sites that don’t require client-side JavaScript, which I agree is a major trend that has all sorts of positive fall-out. And I love it. I generally think of progressive enhancement as developers building interactive components — components that require tech that not all browsers support — and making sure they have some baseline functionality, even if JavaScript, or whatever other tech, fails. I never see that grow in popularity. I think it’s related to the things Melanie talks about here.
  • Functional JavaScript. Wild card choice! I like it! I can’t opine, but this is the kind of thing Jay is good at when it comes to front-end predictions.

To Shared LinkPermalink on CSS-Tricks

(Jay Freestone’s) Front-End Predictions for 2022 originally published on CSS-Tricks. You should get the newsletter.

Subsetting Font Awesome to Improve Performance

Css Tricks - Thu, 02/17/2022 - 5:21am

Font Awesome is an incredibly popular icon library. Unfortunately, it’s somewhat easy to use in a way that results in less-than-ideal performance. By subsetting Font Awesome, we can remove any unused glyphs from the font files it provides. This will reduce the number of bytes transmitted over the wire, and improve performance.

Let’s subset fonts together in a Font Awesome project to see the difference it makes. As we go, I’ll assume you’re importing the CSS file Font Awesome provides, and using its web fonts to display icons.

Let’s set things up

For the sake of demonstration, I have nothing but an HTML file that imports Font Awesome’s base CSS file. To get a reasonable sample of icons, I’ve listed out each one that I use on one of my side projects.

Here’s what our HTML file looks like in the browser before subsetting fonts:

Here’s a look at DevTool’s Network tab to see what’s coming down.

Now let’s see how many bytes our font files take to render all that.

Here’s our base case

We want to see what the most straightforward, least performant use of Font Awesome looks like. In other words, we want the slowest possible implementation with no optimization. I’m importing the all.min.css file Font Awesome provides.

As we saw above, the gzipped file weighs in at 33.4KB, which isn’t bad at all. Unfortunately, when we peek into DevTool’s Font tab, things get a little worse.

Yikes. 757KB just for font files. For 54 icons.

While font files are not as expensive a resource for your browser to handle as JavaScript, those are still bytes your browser needs to pull down, just for some little icons. Consider that some of your users might be browsing your site on mobile, away from a strong or fast internet connection.

First attempt using PurifyCSS

Font Awesome’s main stylesheet contains definitions for literally thousands of icons. But what if we only need a few dozen at most? Surely we could trim out the unneeded stuff?

There are many tools out there that will analyze your code, and remove unused styles from a stylesheet. I happen to be using PurifyCSS. While this library isn’t actively maintained anymore, the idea is the same, and in the end, this isn’t the solution we’re looking for. But let’s see what happens when we trim our CSS down to only what’s needed, which we can do with this script:

const purify = require("purify-css"); const content = ["./dist/**/*.js"]; // Vite-built content purify(content, ["./css/fontawesome/css/all.css"], { minify: true, output: "./css/fontawesome/css/font-awesome-minimal-build.css" });

And when we load that newly built CSS file, our CSS bytes over the wire drop quite a bit, from 33KB to just 7.1KB!

But unfortunately, our other Font Awesome font files are unchanged.

What happened? PurifyCSS did its job. It indeed removed the CSS rules for all the unused icons. Unfortunately, it wasn’t capable of reaching into the actual font files to trim down the glyphs, in addition to the CSS rules.

If only there was a tool like PurifyCSS that handles font files…

Subsetters to the rescue!

There are, of course, tools that are capable of removing unused content from font files, and they’re called subsetters. A subsetter analyzes your webpage, looks at your font files, and trims out the unused characters. There are a bunch of tools for subsetting fonts out there, like Zach Leatherman’s Glyphhanger. As it turns out, subsetting Font Awesome is pretty straightforward because it ships its own built-in subsetters. Let’s take a look.

Subsetting fonts automatically

The auto subsetting and manual subsetting tools I’m about to show you require a paid Font Awesome Pro subscription.

Font Awesome allows you to set up what it calls kits, which are described in the Font Awesome docs as a “knapsack that carries all the icons and awesomeness you need in a neat little lightweight bundle you can sling on the back of your project with ease.” So, rather than importing any and every CSS file, a kit gives you a single script tag you can add to your HTML file’s <head>, and from there, the kit only sends down the font glyphs you actually need from the font file.

Creating a kit takes about a minute. You’re handed script tag that looks something like this:

<script src="https://kit.fontawesome.com/xyzabc.js" crossorigin="anonymous"></script>

When the script loads, we now have no CSS files at all, and the JavaScript file is a mere 4KB. Let’s look again at the DevTools Fonts tab to see which font files are loaded now that we’ve done some subsetting.

We’ve gone from 757KB down to 331KB. That’s a more than 50% reduction. But we can still do better than that, especially if all we’re rendering is 54 icons. That’s where Font Awesome’s manual font subsetter comes into play.

Subsetting fonts manually

Wouldn’t it be nice if Font Awesome gave us a tool to literally pick the exact icons we wanted, and then provide a custom build for that? Well, they do. They don’t advertise this too loudly for some reason, but they actually have a desktop application exactly for subsetting fonts manually. The app is available to download from their site — but, like the automatic subsetter, this app requires a paid Font Awesome subscription to actually use.

Search the icons, choose the family, add what you want, and then click the big blue Build button. That’s really all it takes to generate a custom subset of Font Awesome icons.

Once you hit the button, Font Awesome will ask where it should save your custom build, then it dumps a ZIP file that contains everything you need. In fact, the structure you’ll get is exactly the same as the normal Font Awesome download, which makes things especially simple. And naturally, it lets you save the custom build as a project file so you can open it back up later to add or remove icons as needed.

We’ll open up DevTools to see the final size of the icons we’re loading, but first, let’s look at the actual font files themselves. The custom build creates many different types, depending on what your browser uses. Let’s focus on the .woff2 files, which is what Chrome loads. The same light, regular, duotone, solid, and brand files that were there before are still in place, except this time no file is larger than 5KB… and that’s before they’re gzipped!

And what about the CSS file? It slims down to just 8KB. With gzip, it’s only 2KB!

Here’s the final tally in DevTools:

Before we go, take a quick peek at those font filenames. The fa-light-300.woff2 font file is still there, but the others look different. That’s because I’m using Vite here, and it decided to automatically inline the font files into the CSS, since they’re so tiny.

That’s why our CSS file looks a little bigger in the DevTools Network tab than the 2KB we saw before on disk. The tradeoff is that most of those font “files” from above aren’t files at all, but rather Base64-encoded strings embedded right in this CSS file, saving us additional network requests.

All that said, Vite is inlining many different font formats that the browser will never use. But overall it’s a pretty small number of bytes, especially compared to what we were seeing before.

Before leaving, if you’re wondering whether that desktop font subsetting GUI tool comes in a CLI that can integrate with CI/CD to generate these files at build time, the answer is… not yet. I emailed the Font Awesome folks, and they said something is planned. That’ll allow users to streamline their build process if and when it ships.

As you’ve seen, using something like Font Awesome for icons is super cool. But the default usage might not always be the best approach for your project. To get the smallest file size possible, subsetting fonts is something we can do to trim what we don’t need, and only serve what we do. That’s the kind of performance we want, especially when it comes to loading fonts, which have traditionally been tough to wrangle.

Subsetting Font Awesome to Improve Performance originally published on CSS-Tricks. You should get the newsletter.

Top Things You Didn’t Know You Could Do With Netlify CLI

Css Tricks - Thu, 02/17/2022 - 3:00am

(This is a sponsored post.)

First things first, if you didn’t know Netlify had a CLI, they do.  One of my favorite things about it running the command netlify dev on nearly any static-site generator project is seeing it detect what it should be doing and spinning the site up in a dev server for you. But not just any dev server, a dev server that replicates the Netlify environment, meaning things like running your serverless functions and making your environment variables available.

Here are five more things you can do with it that you might not realize.

1) Create a new site from a template

That’s right, spin up a new site by typing a single command and walking through the steps. Try it:

netlify sites:create-template

There is a shorthand to the CLI as well! Try the above as ntl sites:create-template

As Charlie Gerard writes in a blog post about this:

At the moment, our templates include a Gatsby and Hugo starter with the Netlify CMS, as well as a Next.js starter. 

2) Manage your environment variables

The netlify env command, now in Beta, allows you to control environment variables. You can list them out with netlify env:list, get and set (and unset) them. My favorite: move a whole set of them from one site to another like netlify env:migrate --to <to-site-id>.

3) Test serverless functions

By virtue of spinning up your site locally with the Netlify CLI, your serverless functions will run. You can test that they are working and inspect the network traffic and such that way. But the CLI can help you as well, the netlify functions command is capable of testing functions at the command line level. For example, netlify functions:invoke can trigger a function with simulated data.

4) Live stream your Dev environment

Here’s Melanie Crissey on the Netlify Blog about this:

While Netlify’s collaborative Deploy Previews are our go-to for asynchronous feedback, sometimes you need to drop everything and pair on an issue together. That’s when Netlify Live really shines.

For example, just last week, our team was working quickly to debug some funky edge case issues with authentication for the Your Year on Netlify project. Zach Leatherman, who was working on the fix, spun up a local version of the app with Netlify Live. Within minutes, he was able to see the logs, identify the issue, and make a few changes. Meanwhile, I was able to test out the fix before it was ever deployed—without pulling down a copy of his latest version from a repo. Netlify CLI to the rescue and problem solved!

Remember how I mentioned you spin up a dev environment locally with netlify dev? The trick here is to do netlify dev --live. So rather than a localhost URL that only you would be able to see, you’ll get a special netlify.live URL that the world can see.

5) Run netlify switch to switch between different Netlify accounts, like from your personal side project to a work project

You literally auth with the CLI (netlify login, imagine that), so that you can act on behalf of your own Netlify account. Deploy sites and whatnot. But it’s perfectly reasonable that you have multiple Netlify accounts (like work and personal). Running netlify switch makes it trivial to move between accounts.


This video is 50 seconds long and shows how you can go from having some static files locally to a deployed with the CLI:

Top Things You Didn’t Know You Could Do With Netlify CLI originally published on CSS-Tricks. You should get the newsletter.

Testing: User, Usability, and Others You Should Be Using

Usability Geek - Thu, 02/17/2022 - 2:25am
When it comes to testing your user experiences, there are plenty of methods you can use that will get you the information you need. From interviews to assistive technology testing, these methods...
Categories: Web Standards

Add-to-Calendar Button UI Widget

Css Tricks - Wed, 02/16/2022 - 3:14pm

A useful little UI widget thingy here from Jens Kuerschner. Click the add-to-calendar button, get a list of calendar apps, the user selects which one they actually use, and they get what they need for that calendar. Could be a specialized URL they get sent to, or even an .ics file that gets downloaded.

It’s pretty easy to use. Here’s me using the library off of CDNs for both the JavaScript and CSS:

CodePen Embed Fallback

Let’s do a thought dump!

The configuration as “a big chunk of JSON sitting in the HTML as a string” is a little weird to me.

I see the hack where it uses display: none; on the parent to hide that text from rendering, but I think I like the setup where that’s put into a <script type="application/ld+json"> tag much better.

The fallback for these, assuming JavaScript doesn’t load or execute correctly, is nothing.

I’m torn there. Maybe it’s fine? This seems like bonus functionality anyway. And it’s presumably sitting next to actual content about the event that a user could add to their calendar however they want. I certainly wouldn’t want to see non-interactive text saying “Add to Calendar” because that’s worse than nothing. But maybe there could be some kind of generically useful hyperlink that can act as the fallback?

An add-to-calendar button seems like a good use case for a web component.

Why not an <add-to-calendar> element? That way, the script and styles could be isolated and probably a bit safer for general usage. But how do you do JSON config for a web component? Maybe every single property becomes an attribute? Maybe something like: <add-to-calendar options="Apple, Google", startTime="10:15" />

The biggest problem to address up front, though, is that it looks like the interactive element is a <div> with all JavaScript handlers.

You can’t Tab to it at all, so there is no way to activate it. There are no CSS states — it’s all classes updated by JavaScript. I’d definitely get this thing updated to be a <button>. And maybe it’s good timing to make use of a <dialog> element for the options and use dialog::backdrop for that fancy backdrop-filter background.

Just some constructive criticism, Jens — keep on keepin’ on.

To Shared LinkPermalink on CSS-Tricks

Add-to-Calendar Button UI Widget originally published on CSS-Tricks. You should get the newsletter.

An Auto-Filling CSS Grid With Max Columns of a Minimum Size

Css Tricks - Wed, 02/16/2022 - 5:06am

Within Drupal 10 core, we’re implementing a new auto-filling CSS Grid technique that I think is cool enough to share with the world.

The requirements are:

  • The user specifies a maximum number of columns. This is the auto-filling grid’s “natural” state.
  • If a grid cell goes under a user-specified width, the auto-filling grid will readjust itself and decrease the number of columns.
  • The grid cells should always stretch to fit the auto-filling grid container’s width, no matter the column count.
  • All of this should work independent of viewport width and should not require JavaScript.
The auto-filling CSS Grid in action

Here’s how the resulting auto-filling CSS grid behaves when it is compressed by the draggable div element to its left.

Here’s the code

If you’re not looking for the theory behind the auto-filling grid, and just want to copy/paste code, here you go!

.grid-container { /** * User input values. */ --grid-layout-gap: 10px; --grid-column-count: 4; --grid-item--min-width: 100px; /** * Calculated values. */ --gap-count: calc(var(--grid-column-count) - 1); --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap)); --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count)); display: grid; grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr)); grid-gap: var(--grid-layout-gap); } CodePen Embed Fallback Theory and tools behind the auto-filling CSS Grid

The code above uses several modern CSS tools including CSS Grid’s repeat(), auto-fill(), and minmax() functions, as well as the CSS max(), and calc() functions. Here’s how it works.

CSS Grid’s auto-fill() function

The key to all of this is auto-fill(). We need each row to fill up with as many columns as possible. For more info on auto-fill, check out Sara Soueidan’s awesome article on the difference between auto-fill and auto-fit, which includes this helpful video showing how it works.

But how to we make sure that it doesn’t fill in too many columns?

The CSS max() function

That’s where the max() function comes in! We want each grid cell’s width to max out at a certain percentage, say 25% for a four-column grid. But, we can’t have it go below the user-specified minimum width.

So, assuming a four-column grid and minimum cell width of 100px, the max() function would look something like: max(25%, 100px).

However, the 25% value is still not quite correct because it doesn’t take the grid gaps into account. What we really need is something like this instead:

max(calc(25% - <grid-gap-for-one-cell>), 100px)

We can calc()-ulate this in CSS! (Who says CSS isn’t programming?)

--gap-count: calc(var(--grid-column-count) - 1); --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap)); --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

Now we have another key to making this work! This will tell the grid cell to go to its maximum width — which takes into account the user-specified columns) — but will never go under 100px.

max(100px, var(--grid-item--max-width))

Learn more about the max() function with Chris Coyier’s article on the CSS min(),max(), and clamp() functions.

CSS Grid’s minmax() function

We’re getting close, but there’s one key ingredient that’s missing: The grid doesn’t always stretch to its parent’s container’s width.

This is exactly what the minmax() function is designed to do. The following CSS will set the minimum width to the <grid-item-width>, and if it has room, it’ll stretch all the cells out equally to fit the parent’s width!

minmax(<grid-item-width>, 1fr) Let’s put it all together and make some magic!

Using the tools above, we can put together this magic bit of code that does exactly what we want!

--gap-count: calc(var(--grid-column-count) - 1); --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap)); --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count)); grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr)); CSS is fun!

CSS has really come a long way. I had a lot of fun working on this, and I’m so happy that use-cases like this are now possible without the use of JavaScript.

Special thanks to Andy Blum, who suggested auto-fill() over auto-fit(). Also, an extremely special thanks to all of the implementors and spec writers who make advanced functions like this standardized and possible.

An Auto-Filling CSS Grid With Max Columns of a Minimum Size originally published on CSS-Tricks. You should get the newsletter.

GSAP Flip Plugin for Animation

Css Tricks - Tue, 02/15/2022 - 11:41am

Greensock made the GSAP Flip plugin free in the 3.9 release. FLIP is an animation concept that helps make super performance state-change animations. Ryan Mulligan has a good blog post:

FLIP, coined by Paul Lewis, is an acronym for First, Last, Invert, and Play. The Flip plugin harnesses this technique so that web developers can effortlessly and smoothly transition elements between states.

Examples using the GSAP Flip plugin

Taking advantage of FLIP “by hand” is certainly possible, but tricky. It’s an absolutely perfect thing for an animation library to do for us. Greenstock nailed it, as Ryan says:

1. Get the current state
2. Make your state changes
3. Call Flip.from(state, options)

Deliciously simple. Ryan made an “add to cart” effect with it:

CodePen Embed Fallback

I used it just the other day to make a “mini photo gallery” that could rotate which image was the big one on top:

CodePen Embed Fallback

Which, coincidently, is exactly why I ended up blogging “How to Cycle Through Classes on an HTML Element” the other day.

To Shared LinkPermalink on CSS-Tricks

GSAP Flip Plugin for Animation originally published on CSS-Tricks. You should get the newsletter.

6 Creative Ideas for CSS Link Hover Effects

Css Tricks - Tue, 02/15/2022 - 5:37am

Creating CSS link hover effects can add a bit of flair to an otherwise bland webpage. If you’ve ever found yourself stumped trying to make a slick hover effect, then I have six CSS effects for you to take and use for your next project.

Let’s get right to it!

I know we’re talking about :hover and all, but it can sometimes (but maybe not always) be a good idea lump :focus in as well, as not all interactions are directly from a mouse, but perhaps a tap or keystroke.

The Sliding Highlight Link Hover Effect

This effect applies a box shadow to the inline link, altering the color of the link text in the process. We start with padding all around the link, then add a negative margin of the same value to prevent the padding from disrupting the text flow.

We will use box-shadow instead of the background property since it allows us to transition.

a { box-shadow: inset 0 0 0 0 #54b3d6; color: #54b3d6; margin: 0 -.25rem; padding: 0 .25rem; transition: color .3s ease-in-out, box-shadow .3s ease-in-out; } a:hover { box-shadow: inset 100px 0 0 0 #54b3d6; color: white; } CodePen Embed Fallback The Text Swappin’ Link Hover Effect

Here’s a fun one where we swap the text of the link with some other text on hover. Hover over the text and the linked text slides out as new text slides in.

Easier to show than tell.

There’s quite a bit of trickery happening in this link hover effect. But the magic sauce is using a data-attribute to define the text that slides in and call it with the content property of the link’s ::after pseudo-element.

First off, the HTML markup:

<p>Hover <a href="#" data-replace="get a link"><span>get a link</span></a></p>

That’s a lot of inline markup, but you’re looking at a paragraph tag that contains a link and a span.

Let’s give link some base styles. We need to give it relative positioning to hold the pseudo-elements — which will be absolutely positioned — in place, make sure it’s displayed as inline-block to get box element styling affordances, and hide any overflow the pseudo-elements might cause.

a { overflow: hidden; position: relative; display: inline-block; }

The ::before and ::after pseudo-elements should have some absolute positioning so they stack with the actual link. We’ll make sure they are set to the link’s full width with a zero offset in the left position, setting them up for some sliding action.

a::before, a::after { content: ''; position: absolute; width: 100%; left: 0; }

The ::after pseudo-element gets the content from the link’s data-attribute that’s in the HTML markup:

a::after { content: attr(data-replace); }

Now we can transform: translate3d() the ::after pseudo-element element to the right by 200%. We move it back into position on :hover. While we’re at it, we can give this a zero offset n the top direction. This’ll be important later when we use the ::before pseudo-element like an underline below the text.

a::after { content: attr(data-replace); top: 0; transform-origin: 100% 50%; transform: translate3d(200%, 0, 0); } a:hover::after, a:focus::after { transform: translate3d(0, 0, 0); }

We’re also going to transform: scale() the ::before pseudo-element so it’s hidden by default, then scale it back up on :hover. We’ll make it small, like 2px in height, and pin it to the bottom so it looks like an underline on the text that swaps in with ::after.

a::before { background-color: #54b3d6; height: 2px; bottom: 0; transform-origin: 100% 50%; transform: scaleX(0); } a:hover::before, a:focus::before { transform-origin: 0% 50%; transform: scaleX(1); }

The rest is all preference! We drop in a transition on the transform effects, some colors, and whatnot to get the full effect. Those values are totally up to you.

CodePen Embed Fallback View full CSS a { overflow: hidden; position: relative; display: inline-block; } a::before, a::after { content: ''; position: absolute; width: 100%; left: 0; } a::before { background-color: #54b3d6; height: 2px; bottom: 0; transform-origin: 100% 50%; transform: scaleX(0); transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1); } a::after { content: attr(data-replace); height: 100%; top: 0; transform-origin: 100% 50%; transform: translate3d(200%, 0, 0); transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1); color: #54b3d6; } a:hover::before { transform-origin: 0% 50%; transform: scaleX(1); } a:hover::after { transform: translate3d(0, 0, 0); } a span { display: inline-block; transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1); } a:hover span { transform: translate3d(-200%, 0, 0); } The Growing Background Link Hover Effect

This is a pretty popular effect I’ve seen used in quite a few places. The idea is that you use the link’s ::before pseudo-element as a thick underline that sits slightly behind the actual text of the link. Then, on hover, the pseudo-element expands to cover the whole thing.

OK, some base styles for the link. We want no text-decoration since ::before will act like one, then some relative positioning to hold ::before in place when we give that absolute positioning.

a { text-decoration: none; position: relative; }

Now let’s set up ::before by making it something like 8px tall so it looks like a thick underline. We’ll also give it absolute positioning so we have control to make it the full width of the actual link while offsetting it so it’s at the left and is just a smidge off the bottom so it looks like it’s subtly highlighting the link. May as well give it z-index: -1 so we’re assured it sits behind the link.

a::before { content: ''; background-color: hsla(196, 61%, 58%, .75); position: absolute; left: 0; bottom: 3px; width: 100%; height: 8px; z-index: -1; }

Nice, nice. Let’s make it appear as though ::before is growing when the link is hovered. All we need is to change the height from 3px to 100%. Notice that I’m also dropping the bottom offset back to zero so the background covers more space when it grows.

a:hover::before { bottom: 0; height: 100%; }

Now for slight transition on those changes:

a::before { content: ''; background-color: hsla(196, 61%, 58%, .75); position: absolute; left: 0; bottom: 3px; width: 100%; height: 8px; z-index: -1; transition: all .3s ease-in-out; } CodePen Embed Fallback View full CSS a { text-decoration: none; color: #18272F; font-weight: 700; position: relative; } a::before { content: ''; background-color: hsla(196, 61%, 58%, .75); position: absolute; left: 0; bottom: 3px; width: 100%; height: 8px; z-index: -1; transition: all .3s ease-in-out; } a:hover::before { bottom: 0; height: 100%; } The Right-to-Left Color Swap Link Hover Effect

I personally like using this effect for links in a navigation. The link starts in one color without an underline. Then, on hover, a new color slides in from the right while an underline slides in from the left.

Neat, right? There’s a lot of motion happening in there, so you might consider the accessibility implications and wrap it all in a prefers-reduced-motion query to replace it with something more subtle for those with motion sensitivities.

Here’s how it works. We give the link a linear background gradient with a hard stop between two colors at the halfway mark.

a { background-image: linear-gradient( to right, #54b3d6, #54b3d6 50%, #000 50% ); }

We make the background double the link’s width, or 200%, and position it all the way over to the left. That way, it’s like only one of the gradients two colors is showing.

a { background-image: linear-gradient( to right, #54b3d6, #54b3d6 50%, #000 50% ); background-size: 200% 100%; background-position: -100%; }

The magic happens when we reach for a couple of non-standard -webkit-prefixed properties. One strips the color out of the text to make it transparent. The other clips the background gradient to the text so it appears the text is actually the color of the background.

a { background-image: linear-gradient( to right, #54b3d6, #54b3d6 50%, #000 50% ); background-size: 200% 100%; background-position: -100%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; }

Still with me? Now let’s make the link’s faux underline by putting ::before to use. We’ll give it the same color we gave the on the hidden portion of the link’s background gradient and position it under the actual link so it looks like a proper text-decoration: underline.

a:before { content: ''; background: #54b3d6; display: block; position: absolute; bottom: -3px; left: 0; width: 0; height: 3px; }

On hover, we slide ::before into place, coming in from the left:

a:hover { background-position: 0; }

Now, this is a little tricky. On hover, we make the link’s ::before pseudo-element 100% of the link’s width. If we were to apply this directly to the link’s hover, we’d make the link itself full-width, which moves it around the screen. Yikes!

a:hover::before { width: 100%; }

Add a little transition to smooth things out:

a { background-image: linear-gradient( to right, #54b3d6, #54b3d6 50%, #000 50% ); background-size: 200% 100%; background-position: -100%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; transition: all 0.3s ease-in-out; } CodePen Embed Fallback View full CSS a { background-image: linear-gradient( to right, #54b3d6, #54b3d6 50%, #000 50% ); background-size: 200% 100%; background-position: -100%; display: inline-block; padding: 5px 0; position: relative; -webkit-background-clip: text; -webkit-text-fill-color: transparent; transition: all 0.3s ease-in-out; } a:before { content: ''; background: #54b3d6; display: block; position: absolute; bottom: -3px; left: 0; width: 0; height: 3px; transition: all 0.3s ease-in-out; } a:hover { background-position: 0; } a:hover::before { width:100%; } The Rainbow Underline Link Hover Effect

We can’t do text-decoration-color: rainbow, but we can fake it with a little background magic mixed with linear gradients.

First, we remove the link’s text-decoration:

a { text-decoration: none; }

Now for those gradients. We chain two linear gradients together on the same background property. One gradient is the initial color before hover. The second is the rainbow on hover.

a { background: linear-gradient( to right, rgba(100, 200, 200, 1), rgba(100, 200, 200, 1) ), linear-gradient( to right, rgba(255, 0, 0, 1), rgba(255, 0, 180, 1), rgba(0, 100, 200, 1) ); }

Let’s make the background size a mere 3px tall so it looks like, you know, an underline. We can size both gradients together on the background-size property so that the initial gradient is full width and 3px tall, and the rainbow is zero width.

a { background: linear-gradient( to right, rgba(100, 200, 200, 1), rgba(100, 200, 200, 1) ), linear-gradient( to right, rgba(255, 0, 0, 1), rgba(255, 0, 180, 1), rgba(0, 100, 200, 1) ); background-size: 100% 3px, 0 3px; }

Now we can position the background gradients — at the same time on the background-position property — so that the first gradient is fully in view and the rainbow is pushed out of view. Oh, and let’s make sure the background isn’t repeating while we’re at it.

a { background: linear-gradient( to right, rgba(100, 200, 200, 1), rgba(100, 200, 200, 1) ), linear-gradient( to right, rgba(255, 0, 0, 1), rgba(255, 0, 180, 1), rgba(0, 100, 200, 1) ); background-size: 100% 3px, 0 3px; background-position: 100% 100%, 0 100%; background-repeat: no-repeat; }

Let’s update the background-size on hover so that the gradients swap values:

a:hover { background-size: 0 3px, 100% 3px; }

And, finally, a little transition when the hover takes place:

a { background: linear-gradient( to right, rgba(100, 200, 200, 1), rgba(100, 200, 200, 1) ), linear-gradient( to right, rgba(255, 0, 0, 1), rgba(255, 0, 180, 1), rgba(0, 100, 200, 1) ); background-size: 100% 3px, 0 3px; background-position: 100% 100%, 0 100%; background-repeat: no-repeat; transition: background-size 400ms; }


CodePen Embed Fallback The Passing Underline Link Hover Effect

Geoff Graham actually covered this same one recently when he dissected Adam Argyle’s slick hover effect. In his demo, a background color enters from the left behind the link, then exits to the right on mouse out.

My version pares down the background so it’s more of an underline.

a { position: relative; } a::before { content: ''; position: absolute; width: 100%; height: 4px; border-radius: 4px; background-color: #18272F; bottom: 0; left: 0; transform-origin: right; transform: scaleX(0); transition: transform .3s ease-in-out; } a:hover::before { transform-origin: left; transform: scaleX(1); } CodePen Embed Fallback

That’s not the only way to accomplish this! Here’s another one by Justin Wong using background instead:

CodePen Embed Fallback

Geoff also has a roundup of CSS link hover effects, ranging from neat to downright absurd. Worth checking out!

Have a blast linking!

There are a lot of options when it comes to creating your own hover effect for in-line links with CSS. You can even play with these effects and create something new. I hope you liked the article. Keep experimenting!

6 Creative Ideas for CSS Link Hover Effects originally published on CSS-Tricks. You should get the newsletter.

WordPress.com Has a New Home on YouTube

Css Tricks - Tue, 02/15/2022 - 5:35am

(This is a sponsored post.)

✋ High fives to WordPress for releasing version 5.9 on January 29! This was the long-awaited introduction of the Site Editor and the reverberations are still being felt across the 43% slice of the web that is powered by WordPress.

The Site Editor is more than a neat feature: it’s a completely new approach to theming in WordPress. What makes it a big deal is that it lowers what was once a pretty high barrier to entry for anyone who wants to create or customize a WordPress theme, thanks to a visual interface that takes the PHP out of everything. If you’re interested more in this transition, check out Ganesh Dahal’s Deep Introduction to WordPress Block Themes.

Need a new template? All it takes is a click and dropping some blocks into place. Learn the Site Editor on WordPress.com’s YouTube Page

The Site Editor, like many things about WordPress, is intuitive as heck. But it’s still such a new concept that it might be worth getting a few pointers on how to use it.

That’s why the WordPress.com team set up a brand spankin’ new YouTube channel full of fresh videos that walk you through it, including how full-site editing works, how to set up a homepage, and much more.

The idea is that this WordPress.com YouTube channel can be your go-to for all sorts of educational resources to support your ongoing website-building needs. There’s already a good amount of content in there with plans for more videos released regularly.

And just because the videos center around WordPress.com, anyone running a WordPress site, self-hosted or not, will benefit from these step-by-step tutorials.

Subscribe on YouTube

WordPress.com Has a New Home on YouTube originally published on CSS-Tricks. You should get the newsletter.

Most Popular Wireframe tools Small Business Should Consider in 2022

Usability Geek - Tue, 02/15/2022 - 12:43am
Wireframing is the first and the most crucial step in deciding the fate of an application. The right wireframe can make an excellent app – turning an idea perfectly into an app, while a wrong...
Categories: Web Standards

Why are hyperlinks blue?

Css Tricks - Mon, 02/14/2022 - 10:13am

Last year, Elise Blanchard did some great historical research and discovered that blue hyperlinks replaced black hyperlinks in 1993. They’ve been blue for so long now that the general advice I always hear is to keep them that way. There is powerful societal muscle memory for “blue text is a clickable link.”


On a hot tip, Elise kept digging and published a follow-up and identified the source of blue hyperlinks:

[…] it is Prof. Ben Shneiderman whom we can thank for the modern blue hyperlink.

But it didn’t start on the web. It was more about operating systems in the very early 1990s that started using blue for interactive components and highlighted text.

The decision to make hyperlinks blue in Mosaic, and the reason why we see it happening in Cello at the same time, is that by 1993, blue was becoming the industry standard for interaction for hypertext. It had been eight years since the initial research on blue as a hyperlink color. This data had been shared, presented at conferences, and printed in industry magazines. Hypertext went on to be discussed in multiple forums. Diverse teams’ research came to the same conclusion – color mattered. If it didn’t inspire Marc Andreessen and Eric Bina directly, it inspired those around them and those in their industry.

Because research:

[…] the blue hyperlink was indeed inspired by the research done at the University of Maryland.

To Shared LinkPermalink on CSS-Tricks

Why are hyperlinks blue? originally published on CSS-Tricks. You should get the newsletter.

Getting Started With the File System Access API

Css Tricks - Mon, 02/14/2022 - 6:01am

The File System Access API is a web API that allows read and write access to a user’s local files. It unlocks new capabilities to build powerful web applications, such as text editors or IDEs, image editing tools, improved import/export, all in the frontend. Let’s look into how to get started using this API.

Reading files with the File System Access API

Before diving into the code required to read a file from the user’s system, an important detail to keep in mind is that calling the File System Access API needs to be done by a user gesture, in a secure context. In the following example, we’ll use a click event.

Reading from a single file

Reading data from a file can be done in less than 10 lines of code. Here’s an example code sample:

let fileHandle; document.querySelector(".pick-file").onclick = async () => { [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); const content = await file.text(); return content; };

Let’s imagine we have a button in our HTML with the class .pick-file. When clicking on this button, we launch the file picker by calling window.showOpenFilePicker(), and we store the result from this query in a variable called fileHandle. 

What we get back from calling showOpenFilePicker() is an array of FileSystemFileHandle objects representing each file we selected. As this example is for a single file, we destructure the result. I’ll show how to select multiple files a bit later.

These objects contain a kind and name property. If you were to use console.log(fileHandle), you would see the following object:

FileSystemFileHandle {kind: 'file', name: 'data.txt'}

The kind can either be file or directory.

On fileHandle, we can then call the getFile() method to get details about our file. Calling this method returns an object with a few properties, including a timestamp of when the file was last modified, the name of the file, its size, and type.

Finally, we can call text() on the file to get its content.

Reading from multiple files

To read from multiple files, we need to pass an options object to showOpenFilePicker().

For example:

let fileHandles; const options = { multiple: true, }; document.querySelector(".pick-file").onclick = async () => { fileHandles = await window.showOpenFilePicker(options); // The rest of the code will be shown below };

By default, the multiple property is set to false. Other options can be used to indicate the types of files that can be selected.

For example, if we only wanted to accept .jpeg files, the options object would include the following:

const options = { types: [ { description: "Images", accept: { "image/jpeg": ".jpeg", }, }, ], excludeAcceptAllOption: true, };

In this example, fileHandles is an array containing multiple files, so getting their content would be done in the following way:

let fileHandles; const options = { multiple: true, }; document.querySelector(".pick-file").onclick = async () => { fileHandles = await window.showOpenFilePicker(options); const allContent = await Promise.all( fileHandles.map(async (fileHandle) => { const file = await fileHandle.getFile(); const content = await file.text(); return content; }) ); console.log(allContent); }; Writing to a file with the File System Access API

The File System Access API also allows you to write content to files. First, let’s look into how to save a new file.

Writing to a new file

Writing to a new file can also be done in a very short amount of code!

document.querySelector(".save-file").onclick = async () => { const options = { types: [ { description: "Test files", accept: { "text/plain": [".txt"], }, }, ], }; const handle = await window.showSaveFilePicker(options); const writable = await handle.createWritable(); await writable.write("Hello World"); await writable.close(); return handle; };

If we imagine a second button with the class save-file, on click, we open the file picker with the method showSaveFilePicker() and we pass in an option object containing the type of file to be saved, here a .txt file.

Calling this method will also return a FileSystemFileHandle object like in the first section. On this object, we can call the createWritable() method that will return a FileSystemWritableFileStream object. We can then write some content to this stream with the write() method in which we need to pass the content.

Finally, we need to call the close() method to close the file and finish writing the content to disk.

If you wanted to write some HTML code to a file for example, you would only need to change what’s in the options object to accept "text/html": [".html"]  and pass some HTML content to the write() method.

Editing an existing file

If you’d like to import a file and edit it with the File System Access API,  an example code sample would look like:

let fileHandle; document.querySelector(".pick-file").onclick = async () => { [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); const writable = await fileHandle.createWritable(); await writable.write("This is a new line"); await writable.close(); };

If you’ve been following the rest of this post, you might recognize that we start with the showOpenFilePicker() and getFile() methods to read a file and we then use createWritable(), write() and close() to write to that same file.

If the file you’re importing already has content, this code sample will replace the current content with the new one passed into the write() method.

Additional File System Access API features

Without going into too much detail, the File System Access API also lets you list files in directories and delete files or directories.

Read directories

Reading directories can be done with a tiny bit of code:

document.querySelector(".read-dir").onclick = async () => { const directoryHandle = await window.showDirectoryPicker(); for await (const entry of directoryHandle.values()) { console.log(entry.kind, entry.name); } };

If we add a new button with the class .read-dir, on click, calling the showDirectoryPicker() method will open the file picker and, when selecting a directory on your computer, this code will list the files found in that directory.

Delete files

Deleting a file in a directory can be done with the following code sample:

document.querySelector(".pick-file").onclick = async () => { const [fileHandle] = await window.showOpenFilePicker(); await fileHandle.remove(); };

If you want to delete a folder, you only need to make a small change to the code sample above:

document.querySelector(".read-dir").onclick = async () => { const directoryHandle = await window.showDirectoryPicker(); await directoryHandle.remove(); };

Finally, if you want to remove a specific file when selecting a folder, you could write it like this:

// Delete a single file named data.txt in the selected folder document.querySelector(".pick-folder").onclick = async () => { const directoryHandle = await window.showDirectoryPicker(); await directoryHandle.removeEntry("data.txt"); };

And if you want to remove an entire folder, you would need the following lines:

// Recursively delete the folder named "data" document.querySelector(".pick-folder").onclick = async () => { const directoryHandle = await window.showDirectoryPicker(); await directoryHandle.removeEntry('data', { recursive: true }); }; File System Access API browser support

At the moment, IE and Firefox don’t seem to be supporting the File System Access API. However, there exists a ponyfill called browser-fs-access.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

DesktopChromeFirefoxIEEdgeSafari101NoNo98TPMobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS SafariNoNoNo15.4 Wrapping up

If you’d like to try the File System Access API, check out this live demo text editor built by Google engineers. Otherwise, if you’d like to learn more about this API and all its features, here are some resources:

Getting Started With the File System Access API originally published on CSS-Tricks. You should get the newsletter.

Your CSS reset needs text-size-adjust (probably)

Css Tricks - Fri, 02/11/2022 - 2:46pm

Kilian Valkhof:

[…] Mobile Safari increases the default font-size when you switch a website from portrait to landscape. On phones that is, it doesn’t do it on iPad. Safari has been doing this for a long time, as a way to improve readability on non-mobile optimized websites. While undoubtedly useful in a time when literally no website was optimized for mobile, it’s significantly less helpful nowadays. […] Text size increasing randomly in a single situation is exactly the type of thing you want to guard for with a CSS reset.

This is very literally what text-size-adjust does. MDN:

When an element containing text uses 100% of the screen’s width, the algorithm increases its text size, but without modifying the layout. The text-size-adjust property allows web authors to disable or modify this behavior, as web pages designed with small screens in mind do not need it.

You can see Apple’s own docs showing off this is exactly what they do (on iPhones). There is an ancient bug where this would prevent zooming, but probably not a huge concern anymore.

Kilian’s recommendation:

html { -moz-text-size-adjust: none; -webkit-text-size-adjust: none; text-size-adjust: none; }

Firefox doesn’t even support it, so I’d maybe lose that vendor prefix, but otherwise I’d say I’m on board. I’d like to think I can handle my own text sizing.

Reminds me of how Mobile Safari does that zooming thing with text inputs under 16px, so watch out for that too.

To Shared LinkPermalink on CSS-Tricks

Your CSS reset needs text-size-adjust (probably) originally published on CSS-Tricks. You should get the newsletter.

9 New React and JavaScript Links for February 2022

Css Tricks - Fri, 02/11/2022 - 1:11pm

Every now and then, I find that I’ve accumulated a bunch of links about various things I find interesting. Like React and JavaScript! Here’s a list of nine links to other articles about them that I’ve been saving up and think are worth sharing.

Source: “Good advice on JSX conditionals” by Vladimir Klepov
  • Seed Funding for Remix
    Remix went open source after taking funding which seems like a solid move. It’s a for-now-React-only framework, so I think it’s fair that everyone asks how does it compare to Next.js. Which they answered. Probably worth noting again for us CSS folks, Kent mentioned: “Because Remix allows me to easily control which of my CSS files is on the page at any given time, I don’t have all the problems that triggered the JavaScript community to invent workarounds like CSS-in-JS.”
  • React Router v6
    Speaking of that gang, they released React Router v6, which looks like a positive move — all hooks based, 50% smaller than v5 — but is yet another major version with API changes. React Router has a history of API changes like this and they trigger plenty of grumbling in the community. There is plenty of that again.
  • React Aria
    “A library of React Hooks that provides accessible UI primitives for your design system” from… Adobe. Interesting. Looks like some pretty hard problems being solved here, like FocusScope (“When the contain prop is set, focus is contained within the scope.”) and interesting color inputs, like useColorField, useColorSlider, and useColorWheel. There are 59 hooks in all, ranging from interactions and forms to overlays and internationalization, with plenty of others in between.
  • Front End Tables: Sorting, Filtering, and Pagination
    Tania Rascia: “One thing I’ve had to do at every job I’ve had is implement a table on the front end of an application that has sorting, filtering, and pagination.” No shame in reaching for a big library with all these features, but sometimes it’s best to DIY.
  • Good advice on JSX conditionals
    Vladimir Klepov covers the (weirdly) many ways fairly simple conditionals can go wrong, like the number 0 leaking into your markup, and how to manage update versus remount in conditionals.
  • useProseMirror
    I’ve found ProseMirror to be a pretty nice rich text editor in the past. The library itself isn’t actually in React, so I think it’s a smart call here to make a modern React wrapper for it.
  • Spead up sluggish inputs with useDeferredValue
    You can introduce gnarly input delay the more work that an onChange function has to do on a text input. useDeferredValue gives us a way to separate high priority updates from low priority updates for cases like this.”
  • &#x1f3a5; A Cartoon Intro to WebAssembly
    If you don’t have a good understanding of what WebAssembly is, then Lin Clark will get you there in this video from JSConf EU 2017. So, no, not a new link or anything, but it’s new to me!
  • &#x1f3a5; Turborepo Demo and Walkthrough
    Vercel bought Turborepo. Turborepo is specifically focused on making monorepos better. As someone who’s main codebase is a monorepo with Lerna and Yarn Workspaces such that we can have multiple different sites all share things like a design system, this is right up our alley. This video is with the Turborepo creator Jared Palmer and Lee Robinson, head of developer relations at Vercel. In this video, you get to see it all work.

9 New React and JavaScript Links for February 2022 originally published on CSS-Tricks. You should get the newsletter.

Multi-Value CSS Properties With Optional Custom Property Values

Css Tricks - Fri, 02/11/2022 - 5:30am

Imagine you have an element with a multi-value CSS property, such as transform: optional custom property values:

.el { transform: translate(100px) scale(1.5) skew(5deg); }

Now imagine you don’t always want all the transform values to be applied, so some are optional. You might think of CSS optional custom property values:

.el { /* |-- default ---| |-- optional --| */ transform: translate(100px) var(--transform); }

But surprisingly using optional custom property values like this does not work as intended. If the --transform variable is not defined the whole property will not be applied. I’ve got a little “trick” to fix this and it looks like this:

.el { transform: translate(100px) var(--transform, ); }

Notice the difference? There is a fallback defined in there that is set to an empty value: (, )

That’s the trick, and it’s very useful! Here’s what the specification has to say:

In an exception to the usual comma elision rules, which require commas to be omitted when they’re not separating values, a bare comma, with nothing following it, must be treated as valid in var(), indicating an empty fallback value.

This is somewhat spiritually related to the The CSS Custom Property Toggle Trick that takes advantage of a custom property having the value of an empty space.


Like I said, this is useful and works for any multi-value CSS property. The following demo shows it using text-shadow, background, and filter in addition to the transform example we just discussed.

See the Pen CSS var – Fallback To Nothing by Yair Even Or (@vsync) on CodePen.

Some properties that accept multiple values, like text-shadow, require special treatment because they only work with a comma delimiter. In those cases, when the CSS custom property is defined, you (as the code author) know it is only to be used in a situation where a value is already defined where the custom property is used. Then a comma should be inserted directly in the custom property before the first value, like this:

--text-shadow: ,0 0 5px black;

This, of course, inhibits the ability to use this variable in places where it’s the only value of some property. That can be solved, though, by creating “layers” of variables for abstraction purposes, i.e. the custom property is pointing to lower level custom properties.

Beware of Sass compiler

While exploring this trick, I uncovered a bug in the Sass compiler that strips away the empty value (,) fallback, which goes against the spec. I’ve reported the bug and hope it will be fixed up soon.

As a temporary workaround, a fallback that causes no rendering can be used, such as:

transform: translate(100px) var(--transform, scale(1));

Multi-Value CSS Properties With Optional Custom Property Values originally published on CSS-Tricks. You should get the newsletter.

A Whistle-Stop Tour of 4 New CSS Color Features

Css Tricks - Thu, 02/10/2022 - 1:01pm

I was just writing in my “What’s new in since CSS3?” article about recent and possible future changes to CSS colors. It’s weirdly a lot. There are just as many or more new and upcoming ways to define colors than what we have now. I thought we’d take a really quick look.

First, a major heads up. This stuff is so complicated. I barely understand it. But here are some aspects:

  • Before all this upcoming change, we only had RGB as a color model, and everything dealt with that.
  • We had different “color spaces” that handled it differently (e.g. the rgb() function mapped that RGB color model as a cube with linear coordinates, the hsl() function mapped that RGB color model as a cylinder) but it was all sRGB gamut.
  • With the upcoming changes, we’re getting new color models and (!) we’re getting new functions that map that color model differently. So I think it’s kind of a double-triple whammy.

I can’t personally educate you on all the nitty-gritty details — I’m writing this because I bet there are a lot of you like me, wondering why you should care at all about this, and this is my attempt to understand why I should care about all of it.

Display-P3 is one that opens up a ton of more vibrant color that was able to be expressed before. body { background: color(display-p3 1 0.08 0); /* super red! */ }

It turns out that modern monitors can display way more colors, particularly extra vibrant ones, but we just have no way of defining those colors with classic CSS color syntaxes, like HEX, RGB, and HSL. Super weird, right?! But if you use Display-P3, you get a wider range of access to these vibrant colors.

That white line in Safari DevTools is showing us the “extra” range of Display-P3

The dev shop Panic latched onto this early on and started using these colors as a “secret weapon”:

&#x1f308; Along with WebGL, p3 colors are now a big part of the Panic website secret weapon pile. Shh, don’t tell anyone, but you should see this page on an iMac Pro screen! https://t.co/glrhPNuCdR

— Panic (@panic) May 24, 2019

Jen Simmons also covers how to use them, including a fallback for non-supporting browsers:

Display P3 color. Designing in the browser. Amazing.

Let me show you how to switch over to P3, find a color, and then find a fallback color for older browsers. All while working inside Safari Web Inspector. (Turn sound on to hear me explain!) pic.twitter.com/AaKhrn2s3e

— Jen Simmons (@jensimmons) January 5, 2022 Resources HWB is the one that is more “for humans” except that’s a bit debatable and it’s still based on sRGB.

I had no idea hwb() was a thing — shout out to Stefan Judis for blogging about it.

I normally think of HSL as the CSS color format that is “for humans” (and good for programmatic control) because, well, manipulating 360° of Hue and 0-100% of both Saturation and Lightness make some kind of obvious sense.

But in hwb(), we’ve got Hue (the same as HSL, I think), then Whiteness and Blackness. Stefan:

Adding White and Black to a color affects its saturation. Suppose you add the same amount of White and Black to a color, the color tone stays the same, but color loses saturation. This works up to 50% White and 50% Black (hwb(0deg 50% 50%)), which results in an achromatic color.

Stefan expressed some doubt that this is any easier to understand than HSL, and I tend to agree. I probably just need to get more used to it, but it seems to be more abstract than simply changing the lightness or saturation.

HWB is limited to the same color gamut (sRGB) as all the old color formats all. No new colors are unlocked here.

Resources LAB is like rgb() of a much wider gamut div { background: lab(150% -400 400); }

I liked Eric Portis’ explanation of LAB when I went around asking about it:

LAB is like RGB in that there are three linear components. Lower numbers mean less of the thing, bigger numbers mean more of the thing. So you could use LAB to specify the brightest, greenest green that ever bright-greened, and it’ll be super bright and green for everybody, but brighter and greener on monitors with wider gamuts.

So, we get all the extra color, which is awesome, but sRGB had this other problem (aside from being limited in color expression), that it isn’t perceptually uniform. Brian Kardell:

The sRGB space is not perceptually uniform. The same mathematical movement has different degrees of perceived effect depending on where you are at in the color space. If you want to read a designer’s experience with this, here’s an interesting example which does a good job struggling to do well.

The classic example here is how, in HSL, colors with the exact same “Lightness” really don’t feel the same at all.

HSL vs LAB:: lightness &#x1f4a1;

Same colors from our tricky color poll, but this time I’ve shown LAB’s version of the same color over top. Notice how much closer LAB’s lightness value is to the results of our poll!

&#x1f3a8; color spaces aren’t all the same y’all! https://t.co/AIEs0amdWY pic.twitter.com/xkEguq3KZG

— Adam Argyle (@argyleink) December 3, 2019

But in LAB, apparently, it is perceptually uniform, meaning that programmatically manipulating colors is a much more sane task. And another bonus is that LAB colors are specced as being device-independent. Here’s Michelle Barker:

LAB and LCH are defined in the specification as device-independent colors. LAB is a color space that can be accessed in software like Photoshop and is recommended if you want a color to look the same on-screen as, say, printed on a t-shirt.

Resources LCH is like hsl() of a much wider gamut

Remember how I said HSL is “for humans” in that it is easier understand than RGB? Changing the Hue, Saturation, and Lightness makes a lot of logical sense. Similar here with lch() where we’ve got Lightness, Chroma, and Hue. Back to my conversation with Eric Portis:

LCH is more like HSL: a polar space. H = hue = a circle. So doing math to pick complementary colors (or whatever transforms you’re after) becomes trivial (just add 180 — or whatever!)

I suppose you’d pick LCH just because you like the syntax of it or because it makes some complicated programmatic thing you’re trying to do easier — and you get the fact that it can express 50% more colors for free.

We get the perceptual uniformity here, too. Here’s Lea Verou who seems excited that lightness will actually mean something:

In HSL, lightness is meaningless. Colors can have the same lightness value, with wildly different perceptual lightness. […] With LCH, any colors with the same lightness are equally perceptually light, and any colors with the same chroma are equally perceptually saturated.

Another benefit of the new model is that we can wipe our hands clean of the “gray dead zone” in CSS color gradients. I think because of this perceptual uniformity stuff, two rich colors won’t get cheeky and gradient themselves through non-rich territory.

There will always be tradeoffs in color models, especially with gradients. (Demo)

Here’s a small personal prediction: I’d say that lch() is probably going to be a designer favorite. Soon there are going to be a ton of new color choices and it’s too difficult and weird to always be picking different ones. LCH seems to have the most bang for the mental buck.

Resources “OK”

LAB ‘n’ friends seems so new because it is new… to CSS. But LAB was invented in the 1940s. In a conversation with Adam Argyle, he used a memorable phrase: All the color spaces have an Achilles’ heel. That is, something they kinda suck at. For sRGB, it’s the grey dead zone thing, as well as the limited color gamut. LAB is great and all, but it certainly has its own weaknesses. For example, a blue-to-white gradient in LAB travels pretty awkwardly through purpletown.

In December 2020, Björn Ottosson is all like “Hey, a new color space just dropped,” and now OKLAB exists. Apparently the CSS powers-that-be see enough value in that color space that both oklab() and oklch() are already specced. I guess we should care because they are just generally better, but don’t quote me on that.

Why is it Display P3 uses the color() function but the other’s don’t?

I don’t really know. I think the CSS color() function is a bit newer and that’s just how Safari dunked it in there to start. I have no idea if Display P3 will get its own dedicated function, or if we all should just start using CSS color(), or what.

/* This is how you use Display P3 */ color(display-p3 1 0.08 0); /* But this doesn't work */ color(oklch 42.1% 0.192 328.6); /* You gotta do this instead &#x1f937;‍♀️ */ oklch(42.1% 0.192 328.6); /* But you can use the color space within a gradient... */ background-image: linear-gradient( to right in oklch, lch(50% 100 100), lch(50% 100 250) ); The relative color syntax is super useful.

There is this really cool ability called “relative color syntax” where you can basically deconstruct a CSS color while moving it into another format. Say you have the (obviously) most famous CSS HEX color ever, fog dog, and you wanna kick it into HSL instead:

body { background: hsl(from #f06d06 h s l); }

Maybe that’s not all that useful immediately, but hey, now we’re able to add alpha to it! There is literally no other way to apply alpha to an existing HEX color, so that’s kinda huge:

body { background: hsl(from #f06d06 h s l / 0.5); }

But I can also mess with it. Say I wanna saturate fog dog a bit before I add opacity because the lower opacity will naturally dull it out and I wanna combat that. I can use calc() on the implied variables there:

body { background: hsl(from #f06d06 h calc(s + 20%) l / 0.5); }

That’s so cool. I’m sure we’ll see some amazing things come from this. And it certainly isn’t limited to HSL. I was just using HSL because it’s what is comfortable to me right now. I could start with the named color red and mess with it in LCH if I want:

body { background: lch(from red l calc(c + 15) h / 0.25); }

This stuff is going to be most useful when liberally combined with custom properties.

There are no special functions just for alpha anymore.

Just to be clear: no commas preceding the alpha value in a CSS color function — just a forward slash instead:

/* Old! */ rgb(255, 0, 0); rgba(255, 0, 0, 0.5); /* New! */ rgb(255 0 0); rgb(255 0 0 / 0.5); hsl(0deg 40% 40%) hsl(0deg 40% 40% / 90%) /* can be percentage rather than 0.9 or whatever */ /* The New color stuff ONLY has the single base function, no alpha secondardy function */ lab(49% 39 80) lab(49% 39 80 / 0.25) /* Display P3, with the color function, essentially works the same way with the slash */ color(display-p3 1 0.08 0 / 0.25); You can even define your own CSS color space.

But I literally can’t even think about that. It blows my mind, sorry.

A Whistle-Stop Tour of 4 New CSS Color Features originally published on CSS-Tricks. You should get the newsletter.

Syndicate content
©2003 - Present Akamai Design & Development.