This one is using two@font-face sets for the samefont-family name. The second overrides the first, but only select characters of it, thanks to unicode-range.
You know how designers love ampersands? It’s a thing. Dan Cederholm once pointed out some advice from Robert Bringhurst:
Since the ampersand is more often used in display work than in ordinary text, the more creative versions are often the more useful. There is rarely any reason not to borrow the italic ampersand for use with roman text.
It started with seeing a recent Pen of Mandy Michael’s text effects demos. I’m a very visual creature, so the first thing I noticed was the effect, not the title (which clearly states how the effect was achieved). Instantly, my mind went „blend modes!”, which turned out to be wrong.
The demo actually uses clip-path. First of all, the text is duplicated. We have black text below as the actual text content of the element and the white text above as the value of the content property (taken from a data attribute which gets updated via JS). These two are stacked one on top of each other (they completely overlap). Then the pseudo-element with the white text above gets clipped to the shape of the black dress.
However, this means we need to change the clipping path if we change the image and, at this point, it’s anything but easy to figure out polygonal clipping paths with a lot of points via dev tools (which is why having something like Benett Feely’s Clippy with two-way editing directly in dev tools would be immensely useful). So I decided to give my initial idea – blend modes – a try.
Let’s say we have a heading in a contentEditable1 container with a black and white image (well, grayscale) background. The HTML structure is as follows:
We set the background-image on the header container, we give the h2 white text and set its mix-blend-mode to difference or exclusion. The relevant CSS is below:
I can’t really understand blend modes or normally see a difference between difference and exclusion, but according to MDN, they’re pretty much the same, with exclusion having less contrast. What I can understand in this situation is that having white text over the image gives us a result that’s the image inverted where the text overlaps it. For simple black and white images, black in the original image becomes white where we have white text above it and white in the original image becomes black where we have white text above it.
Mission accomplished! Both the text and the image can be changed and the effect is preserved without the need for any JavaScript or for any changes to the CSS.
But I instantly found that there was another itch to scratch: what happens if the image isn’t just black and white? Well, let’s try that! Turns out the result actually looks pretty good:
However, the text isn’t grayscale anymore and setting filter: grayscale(1) doesn’t change anything. Does any filter value work? Well, tried drop-shadow() next to try to find an answer to this question. And the answer is that, yes, drop-shadow() works, but the way it works – the shadow being blended with the header background2 – provides a clue about why grayscale() didn’t change a thing: filters are applied before blending, our text is white and the output of grayscale() in this case is identical to its input (white in, white out). And, since nothing changes before the blending, its result is the same.
This probably shouldn’t have been a surprise. I’ve ran into issues caused by the order in which properties are applied before.
For example, filter is also applied beforeclip-path, so if we clip an element to a non-rectangular shape and we want to have a drop shadow on it, tough luck, setting the filter on the same element doesn’t give the expected result. This is because the rectangular element gets the filter applied, so we have a drop shadow around its rectangular box and only after that gets clipped to the shape specified via clip-path. This is always the order these two get applied in, regardless of the order you set them in the CSS.
Diagram of how browsers apply filter and clip-path when they’re set on the same element.
The way to solve the filter + clip-path problem is to set the filter on a parent element with nothing visible except the clipped child. The „nothing visible” part is important because, if the parent has some visible text or borders, they’ll get the drop shadow as well, while if it has a background, then the whole background area gets the shadow.
However, this solution doesn’t work for the filter + mix-blend-mode problem as well. Wrapping our h2 into a div and setting filter on that div breaks the blending effect.
What’s worse is that I can’t seem to think of anything I could do to get around this problem. There might be a way of doing it by duplicating the text and using a luminosity blend mode, but, as mentioned before, I don’t really understand blend modes and I haven’t been able to get that right.
But maybe we could try a different approach altogether, one that doesn’t use blending. All the playing with filters gave me the idea that we could apply the image to the text, then apply an invert() filter, to which we could chain grayscale(), contrast() and more.
With this method using background-clip: text, we also have the advantage of a cross-browser solution, since Firefox and Edge have now implemented this too.
The way we go about this is the following: we make sure the header and the h2 have identical backgrounds and that these backgrounds perfectly overlap. Then we set color: transparent on the h2 and clip its background to text. The final step is to set filter: invert(1) on the h2. The relevant CSS3 is as follows:
It looks like before, except it’s using a different method and now we can chain more functions to the filter property. For example, we can make the text grayscale and up its contrast:
Tweaking the filter value is also how we add a text shadow. While in the first case (using mix-blend-mode) we can simply set the text-shadow property (and the shadow also gets blended with the background), doing so in the second case breaks things:
The Pen below shows the two methods described in this article. On the left, we have the mix-blend-mode method and on the right, we have the background-clip and filter method (with the extra grayscale and contrast components). The text is editable and the thumbnails allow for changing the background-image:
So, if we pit these methods against each other, which is better?
When don’t go into aesthetics because it’s a subjective matter and it probably differs from case to case and even from mood to mood.
As far as basic support is concerned, the last method gets an advantage since it’s supported in Edge, while mix-blend-mode isn’t (but if you want it in Edge, please vote for it because your feedback does matter). Mandy’s original example uses clip-path, another very useful feature, which sadly doesn’t work in Edge either (you can vote for it here).
When it comes to selecting text, the mix-blend-mode method works perfectly and looks the best across the browsers it’s supported.
Selecting text when using the mix-blend-mode method
Using the clip-path method, the selection looks clean and the text remains readable, but it seemed to stumble in Firefox while I was over the pseudo-element text. Fortunately, this problem turned out to have an easy fix: setting pointer-events: none on the pseudo-element.
Selecting text in Firefox when using the clip-path method
As for the last method, the selection can look ugly and can make the text harder to read. Not to mention that the drop shadow ends up being applied to the whole selection rectangle in this case, not just to the actual text.
Selecting text when using the background-clip: text + filter method
Changing the text also gets awkward in Firefox with the last method, the screen getting „dirty” as I type new text.
Changing text in Firefox when using the background-clip: text + filter method
The clip-path method also had a problem with changing text in Firefox at first: trying to start editing from the middle of the pseudo-element text didn’t work. Fortunately, setting pointer-events: none on the pseudo-element fixes this as well.
Changing text in Firefox when using the clip-path method
As far as flexibility in terms of how we can alter the text goes, the last method fares best because chaining filter functions can take us a long way.
At the end of the day, which is better ends up depending on the particular use case. What browsers should be supported? Does the image change? Does the text change? How should the text be visually altered?
1contentEditable isn’t accessible in all browsers by default, so we need to provide semantics to fix that.
2 When experimenting with blend modes, be aware of the legibility issues they can produce, particularly regarding contrast. WCAG 2.0 states that, unless the text forms part of purely decorative imagery, it should pass a minimum contrast threshold. Tools like Color ContrastAnalyzer can help you meet that requirement.
3 Prefixes are omitted for brevity, but background-clip still needs the -webkit- prefix for WebKit browsers (Firefox and Edge have implemented it unprefixed, though they both support it with the -webkit- prefix as well, probably because it has already been used to death only with that prefix) and this prefix needs to be added manually/ via a preprocessor mixin, which is something that I normally don’t encourage, but, in this particular case, auto-prefixing via Autoprefixer or Prefixfree doesn’t happen (Prefixfree works via feature detection, which doesn’t catch properties such as background-clip, filter or clip-path and this is the Autoprefixer issue). As for filter, it’s now supported unprefixed in all current desktop browsers and Autoprefixer can prefix it anyway.
Ordered lists are among the oldest and most semantically rich elements in HTML. Anytime you need to communicate sequence or ranking, the <ol> tag is there to help. The default appearance of the <ol> tag presents numbers next to each item in the list. You can use the list-style-type property in CSS to change the default to use Roman numerals or the letters of the alphabet. If you are feeling exotic, you can even use numbering from other cultures like Hebrew or Greek. The full list of available values is well-documented and easy to use.
Recently, I saw an opportunity to use dice in place of numbers for several ordered lists explaining the features of an HTML5 game I created called Triple Score Bopzee. To accomplish my goal, I first experimented with a now-familiar technique for using a small image file as the background for the li::before selector in a list. One change I made to the usual procedure is that I decided to avoid list-style-type: none in favor of using list-style-type: decimal and setting list-style-image to a 1×1 transparent GIF. That small change helps the page pass accessibility tests because screen readers will still see the list as a valid numbered list.
I created a Pen to demonstrate this classic technique using GIFs of numbers that contain the balls used in four major sports.
This technique would have worked for my needs on the bopzee web site but I got curious about how I could do it without using any images. The answer was to use the Unicode symbols \2680 through \2685 for the six dice. I created a class selector called „dicey” and used the nth-child and before pseudo selectors to position and choose the Unicode character for each list item. I added a link to the Normalize.css library to smooth out the subtle browser differences.
<ol class="dicey">
<li>I rolled a one.</li>
<li>I rolled a two.</li>
<li>I rolled a three.</li>
<li>I rolled a four.</li>
<li>I rolled a five.</li>
<li>I rolled a six.</li>
</ol>
This was the trick:
/* Still use a decimal based list for a11y */
ol {
margin-left:40px;
list-style:decimal url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
.dicey li:nth-child(1n):before {position:absolute;left:-1em;}
/* Actually set markers with pseudo element unicode */
.dicey li:nth-child(1):before {content: "\2680";}
.dicey li:nth-child(2):before {content: "\2681";}
.dicey li:nth-child(3):before {content: "\2682";}
.dicey li:nth-child(4):before {content: "\2683";}
.dicey li:nth-child(5):before {content: "\2684";}
.dicey li:nth-child(6):before {content: "\2685";}
Initially, I did not specify the font that would display the Unicode symbols. The result was not bad because today every modern web browser and web enabled device has access to at least one font that contains the necessary symbols from the Unicode block called „Miscellaneous Symbols.” There was some variation in appearance but it was quite tolerable. But I knew that if I wanted real control, I would have to find a free web font. I identified a font called DejaVu that would work by looking through the supported fonts list in the table of glyphs for the Miscellaneous Symbols block.
Once I knew the font name, I was able to create a WebFont kit at Font Squirrel. When creating a WebFont kit, it is important to choose the „No subsetting” option to be sure the fonts contain all the Unicode goodness you need for your icons. Or if you are concerned about file size, you can download the original TrueType or OpenType font, then use Font Squirrel’s WebFont generator in expert mode to include only the Unicode range 2600 to 26FF which captures all characters in the Unicode block called „Miscellaneous Symbols.”
Once you get started with Unicode, the fun never stops. I spent way too much time exploring the many icons that are part of the DejaVu font (complete list). There I found icons for playing card suits, astrological signs, arrows, bullets, musical symbols, geometric shapes, and a whole host of squiggles I can’t begin to name. So I took some of my favorites and created a Pen containing several different kinds of lists.
There is a new CSS rule called @counter-style which will allow us more easily to create custom counter styles for ordered lists, specifying the symbols to be used, the range of the list, and lots of other options. The CSS Counter Styles Level 3 specification is now a Candidate Recommendation at the W3C but as of May 2017 only FireFox supports it (with some issues). I would expect that Chrome, Edge, and Safari will add support sometime soon but the final version of Internet Explorer (version 11) will likely never have it. So if you have to support that browser, you will be stuck with tricks like the one described on this post until people stop using IE11 (perhaps the year 2020?).
Note that the @counter-style rule does not provide any way to style the counter symbol using CSS. So even when it is adopted by all browsers, there may be use cases for alternative solutions like the one presented here. You can read more about @counter-style on MDN. Here is a pen with a demonstration.
We posted not long ago about the difference between native CSS variables (custom properties) and preprocessor variables. There are a few esoteric things preprocessor variables can do that native variables cannot, but for the most part, native variables can do the same things. But, they are more powerful because of how they are live-interpolated. Should their values ever change (e.g. JavaScript, media query hits, etc) the change triggers immediate change on the site.
Cool, right? But still, how actually useful is that? What are the major use cases? I think we’re still seeing those shake out.
One use case, it occurred to me, would be theming of a site (think: custom colors for elements around a site). Rather than writing different CSS for a bunch of different themes, or writing JavaScript that targets all the elements we intend to change and changing them), we just write one base set of CSS that utilizes variables and set those variables to the theme colors.
Imagine we allow the header and footer background of our site to be customized.
The value is plucked out of localStorage and used when the page loads. A default value is also set (in CSS), in case that doesn’t exist.
What makes the above demo so compelling, to me, is how little code it is. Maintaining this as a a feature on a site is largely a CSS endeavour and seems flexible enough to stand the test of time (probably).
Not unusually, I was way behind on this one.
Lots of people think of theming as one of the major use-cases for CSS Custom Properties. Let’s look at some other folks examples.
Giacomo Zinetti has the same kind of color-picker implementation
Harry does a lot of consulting, and to my surprise, finds himself working with companies that want to do this a lot. He warns:
Theming, the vast majority of the time, is a complete nice-to-have. It is not business critical or usually even important. If you are asked to provide such theming, do not do so at the expense of performance or code quality.
They also created react-custom-properties which all about applying Custom Properties to components, taking advantage of the fact that you can set Custom Properties as inline styles:
<div style="--theme-primary-color: blue;">
</div>
More than one color and property
It’s not only colors that can change, a Custom Property can be any valid value. Here’s Keith Clark with a demo with multiple colors as well as font size:
Which meant that you could set those variables and have the component take on new colors.
Support and fallbacks
Support has gotten pretty good recently:
Green indicates full support at the version listed (and above). Yellow indicates partial support. Red indicates no support. See Caniuse for full browser support details.
Desktop
Chrome
Opera
Firefox
IE
Edge
Safari
49
36
31
No
15
9.1
Mobile / Tablet
iOS Safari
Opera Mobile
Opera Mini
Android
Android Chrome
Android Firefox
9.3
37
No
56
57
52
Opera Mini and IE are notably missing. We already covered the idea of a fallback through setting a valid non-variable property before the one using a Custom Property.
Like many modern CSS features, you can use @supports to test for support before using:
It always depends on the situation, but just putting fallbacks on a previous declaration is probably the most useful way to deal with non-support in CSS. There are also edge cases.
When setting colors for text, and the color behind that text, the contrast between those colors is an accessibility issue. Too little contrast, too hard to read.
One somewhat common solution to this is to choose whether the text should be light or dark (white or black) based on the color behind it.
David Halford has a demo calculating this with JavaScript:
Whenever we send or retrieve information with JavaScript, we initiate a thing known as an Ajax call. Ajax is a technique to send and retrieve information behind the scenes without needing to refresh the page. It allows browsers to send and retrieve information, then do things with what it gets back, like add or change HTML on the page.
Let’s take a look at the history of that and then bring ourselves up-to-date.
Another note here, we’re going to be using ES6 syntax for all the demos in this article.
A few years ago, the easiest way to initiate an Ajax call was through the use of jQuery’s ajax method:
$.ajax('some-url', {
success: (data) => { /* do something with the data */ },
error: (err) => { /* do something when an error happens */}
});
We could do Ajax without jQuery, but we had to write an XMLHttpRequest, which is pretty complicated.
Thankfully, browsers nowadays have improved so much that they support the Fetch API, which is a modern way to Ajax without helper libraries like jQuery or Axios. In this article, I’ll show you how to use Fetch to handle both success and errors.
Support for Fetch
Let’s get support out of the way first.
Green indicates full support at the version listed (and above). Yellow indicates partial support. Red indicates no support. See Caniuse for full browser support details.
Desktop
Chrome
Opera
Firefox
IE
Edge
Safari
42
29
39
No
14
10.1
Mobile / Tablet
iOS Safari
Opera Mobile
Opera Mini
Android
Android Chrome
Android Firefox
10.3
37
No
56
57
52
Support for Fetch is pretty good! All major browsers (with the exception of Opera Mini and old IE) support it natively, which means you can safely use it in your projects. If you need support anywhere it isn’t natively supported, you can always depend on this handy polyfill.
Getting data with Fetch
Getting data with Fetch is easy. You just need to provide Fetch with the resource you’re trying to fetch (so meta!).
Let’s say we’re trying to get a list of Chris’ repositories on Github. According to Github’s API, we need to make a get request for api.github.com/users/chriscoyier/repos.
Fetch returns a Promise, which is a way to handle asynchronous operations without the need for a callback.
To do something after the resource is fetched, you write it in a .then call:
fetch('https://api.github.com/users/chriscoyier/repos')
.then(response => {/* do something */})
If this is your first encounter with Fetch, you’ll likely be surprised by the response Fetch returns. If you console.log the response, you’ll get the following information:
{
body: ReadableStream
bodyUsed: false
headers: Headers
ok : true
redirected : false
status : 200
statusText : "OK"
type : "cors"
url : "http://some-website.com/some-url"
__proto__ : Response
}
Here, you can see that Fetch returns a response that tells you the status of the request. We can see that the request is successful (ok is true and status is 200), but a list of Chris’ repos isn’t present anywhere!
Turns out, what we requested from Github is hidden in body as a readable stream. We need to call an appropriate method to convert this readable stream into data we can consume.
Since we’re working with GitHub, we know the response is JSON. We can call response.json to convert the data.
There are other methods to deal with different types of response. If you’re requesting an XML file, then you should call response.text. If you’re requesting an image, you call response.blob.
All these conversion methods (response.json et all) returns another Promise, so we can get the data we wanted with yet another .then call.
fetch('https://api.github.com/users/chriscoyier/repos')
.then(response => response.json())
.then(data => {
// Here's a list of repos!
console.log(data)
});
Phew! That’s all you need to do to get data with Fetch! Short and simple, isn’t it? 🙂
Next, let’s take a look at sending some data with Fetch.
Sending data with Fetch
Sending data with Fetch is pretty simple as well. You just need to configure your fetch request with three options.
fetch('some-url', options);
The first option you need to set is your request method to post, put or del. Fetch automatically sets the method to get if you leave it out, which is why getting a resource takes lesser steps.
The second option is to set your headers. Since we’re primarily sending JSON data in this day and age, we need to set Content-Type to be application/json.
The third option is to set a body that contains JSON content. Since JSON content is required, you often need to call JSON.stringify when you set the body.
In practice, a post request with these three options looks like:
let content = {some: 'content'};
// The actual fetch request
fetch('some-url', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(content)
})
// .then()...
For the sharp-eyed, you’ll notice there’s some boilerplate code for every post, put or del request. Ideally, we can reuse our headers and call JSON.stringify on the content before sending since we already know we’re sending JSON data.
But even with the boilerplate code, Fetch is still pretty nice for sending any request.
Handling errors with Fetch, however, isn’t as straightforward as handling success messages. You’ll see why in a moment.
Handling errors with Fetch
Although we always hope for Ajax requests to be successful, they can fail. There are many reasons why requests may fail, including but not limited to the following:
You tried to fetch a non-existent resource.
You’re unauthorized to fetch the resource.
You entered some arguments wrongly
The server throws an error.
The server timed out.
The server crashed.
The API changed.
…
Things aren’t going to be pretty if your request fails. Just imagine a scenario you tried to buy something online. An error occured, but it remains unhandled by the people who coded the website. As a result, after clicking buy, nothing moves. The page just hangs there… You have no idea if anything happened. Did your card go through? 😱.
Now, let’s try to fetch a non-existent error and learn how to handle errors with Fetch. For this example, let’s say we misspelled chriscoyier as chrissycoyier
// Fetching chrissycoyier's repos instead of chriscoyier's repos
fetch('https://api.github.com/users/chrissycoyier/repos')
We already know we should get an error since there’s no chrissycoyier on Github. To handle errors in promises, we use a catch call.
Given what we know now, you’ll probably come up with this code:
Fetch failed, but the code that gets executed is the second `.then` instead of `.catch`
Why did our second .then call execute? Aren’t promises supposed to handle errors with .catch? Horrible! 😱😱😱
If you console.log the response now, you’ll see slightly different values:
{
body: ReadableStream
bodyUsed: true
headers: Headers
ok: false // Response is not ok
redirected: false
status: 404 // HTTP status is 404.
statusText: "Not Found" // Request not found
type: "cors"
url: "https://api.github.com/users/chrissycoyier/repos"
}
Most of the response remain the same, except ok, status and statusText. As expected, we didn’t find chrissycoyier on Github.
This response tells us Fetch doesn’t care whether your AJAX request succeeded. It only cares about sending a request and receiving a response from the server, which means we need to throw an error if the request failed.
Hence, the initial then call needs to be rewritten such that it only calls response.json if the request succeeded. The easiest way to do so to check if the response is ok.
fetch('some-url')
.then(response => {
if (response.ok) {
return response.json()
} else {
// Find some way to get to execute .catch()
}
});
Once we know the request is unsuccessful, we can either throw an Error or reject a Promise to activate the catch call.
// throwing an Error
else {
throw new Error('something went wrong!')
}
// rejecting a Promise
else {
return Promise.reject('something went wrong!')
}
Choose either one, because they both activate the .catch call.
Here, I choose to use Promise.reject because it’s easier to implement. Errors are cool too, but they’re harder to implement, and the only benefit of an Error is a stack trace, which would be non-existent in a Fetch request anyway.
Failed request, but error gets passed into catch correctly
This is great. We’re getting somewhere since we now have a way to handle errors.
But rejecting the promise (or throwing an Error) with a generic message isn’t good enough. We won’t be able to know what went wrong. I’m pretty sure you don’t want to be on the receiving end for an error like this…
Yeah… I get it that something went wrong… but what exactly? 🙁
What went wrong? Did the server time out? Was my connection cut? There’s no way for me to know! What we need is a way to tell what’s wrong with the request so we can handle it appropriately.
Let’s take a look at the response again and see what we can do:
{
body: ReadableStream
bodyUsed: true
headers: Headers
ok: false // Response is not ok
redirected: false
status: 404 // HTTP status is 404.
statusText: "Not Found" // Request not found
type: "cors"
url: "https://api.github.com/users/chrissycoyier/repos"
}
Okay great. In this case, we know the resource is non-existent. We can return a 404 status or Not Found status text and we’ll know what to do with it.
To get status and statusText into the .catch call, we can reject a JavaScript object:
fetch('some-url')
.then(response => {
if (response.ok) {
return response.json()
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText
})
}
})
.catch(error => {
if (error.status === 404) {
// do something about 404
}
})
Now we’re getting somewhere again! Yay! 😄.
Let’s make this better! 😏.
The above error handling method is good enough for certain HTTP statuses which doesn’t require further explanation, like:
401: Unauthorized
404: Not found
408: Connection timeout
…
But it’s not good enough for this particular badass:
400: Bad request.
What constitutes bad request? It can be a whole slew of things! For example, Stripe returns 400 if the request is missing a required parameter.
Stripe’s explains it returns a 400 error if the request is missing a required field
It’s not enough to just tell our .catch statement there’s a bad request. We need more information to tell what’s missing. Did your user forget their first name? Email? Or maybe their credit card information? We won’t know!
Ideally, in such cases, your server would return an object, telling you what happened together with the failed request. If you use Node and Express, such a response can look like this.
res.status(400).send({
err: 'no first name'
})
Here, we can’t reject a Promise in the initial .then call because the error object from the server can only be read after response.json.
The solution is to return a promise that contains two then calls. This way, we can first read what’s in response.json, then decide what to do with it.
Let’s break the code down. First, we call response.json to read the json data the server sent. Since, response.json returns a Promise, we can immediately call .then to read what’s in it.
We want to call this second .then within the first .then because we still need to access response.ok to determine if the response was successful.
If you want to send the status and statusText along with the json into .catch, you can combine them into one object with Object.assign().
Unfortunately, we’re not done with handling the response just yet 🙁
Handling other response types
So far, we’ve only touched on handling JSON responses with Fetch. This already solves 90% of use cases since APIs return JSON nowadays.
What about the other 10%?
Let’s say you received an XML response with the above code. Immediately, you’ll get an error in your catch statement that says:
Parsing an invalid JSON produces a Syntax error
This is because XML isn’t JSON. We simply can’t return response.json. Instead, we need to return response.text. To do so, we need to check for the content type by accessing the response headers:
.then(response => {
let contentType = response.headers.get('content-type')
if (contentType.includes('application/json')) {
return response.json()
// ...
}
else if (contentType.includes('text/html')) {
return response.text()
// ...
}
else {
// Handle other responses accordingly...
}
});
Wondering why you’ll ever get an XML response?
Well, I encountered it when I tried using ExpressJWT to handle authentication on my server. At that time, I didn’t know you can send JSON as a response, so I left it as its default, XML. This is just one of the many unexpected possibilities you’ll encounter. Want another? Try fetching some-url 🙂
Anyway, here’s the entire code we’ve covered so far:
fetch('some-url')
.then(handleResponse)
.then(data => console.log(data))
.then(error => console.log(error))
function handleResponse (response) {
let contentType = response.headers.get('content-type')
if (contentType.includes('application/json')) {
return handleJSONResponse(response)
} else if (contentType.includes('text/html')) {
return handleTextResponse(response)
} else {
// Other response types as necessary. I haven't found a need for them yet though.
throw new Error(`Sorry, content-type ${contentType} not supported`)
}
}
function handleJSONResponse (response) {
return response.json()
.then(json => {
if (response.ok) {
return json
} else {
return Promise.reject(Object.assign({}, json, {
status: response.status
statusText: response.statusText
}))
}
})
}
function handleTextResponse (response) {
return response.text()
.then(text => {
if (response.ok) {
return json
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText,
err: text
})
}
})
}
It’s a lot of code to write/copy and paste into if you use Fetch. Since I use Fetch heavily in my projects, I create a library around Fetch that does exactly what I described in this article (plus a little more).
Introducing zlFetch
zlFetch is a library that abstracts away the handleResponse function so you can skip ahead to and handle both your data and errors without worrying about the response.
Then, you’ll import it into your code. (Take note of default if you aren’t importing with ES6 imports). If you need a polyfill, make sure you import it before adding zlFetch.
// Polyfills (if needed)
require('isomorphic-fetch') // or whatwg-fetch or node-fetch if you prefer
// ES6 Imports
import zlFetch from 'zl-fetch';
// CommonJS Imports
const zlFetch = require('zl-fetch');
zlFetch does a bit more than removing the need to handle a Fetch response. It also helps you send JSON data without needing to write headers or converting your body to JSON.
The below the functions do the same thing. zlFetch adds a Content-Type and converts your content into JSON under the hood.
let content = {some: 'content'}
// Post request with fetch
fetch('some-url', {
method: 'post',
headers: {'Content-Type': 'application/json'}
body: JSON.stringify(content)
});
// Post request with zlFetch
zlFetch('some-url', {
method: 'post',
body: content
});
zlFetch also makes authentication with JSON Web Tokens easy.
The standard practice for authentication is to add an Authorization key in the headers. The contents of this Authorization key is set to Bearer your-token-here. zlFetch helps to create this field if you add a token option.
So, the following two pieces of code are equivalent.
let token = 'someToken'
zlFetch('some-url', {
headers: {
Authorization: `Bearer ${token}`
}
});
// Authentication with JSON Web Tokens with zlFetch
zlFetch('some-url', {token});
That’s all zlFetch does. It’s just a convenient wrapper function that helps you write less code whenever you use Fetch. Do check out zlFetch if you find it interesting. Otherwise, feel free to roll your own!
Fetch is a piece of amazing technology that makes sending and receiving data a cinch. We no longer need to write XHR requests manually or depend on larger libraries like jQuery.
Although Fetch is awesome, error handling with Fetch isn’t straightforward. Before you can handle errors properly, you need quite a bit of boilerplate code to pass information go to your .catch call.
With zlFetch (and the info presented in this article), there’s no reason why we can’t handle errors properly anymore. Go out there and put some fun into your error messages too 🙂
By the way, if you liked this post, you may also like other front-end-related articles I write on my blog. Feel free to pop by and ask any questions you have. I’ll get back to you as soon as I can.
Stop the browser default behavior of jumping immediately to that element on the page
Animate the scrolling to the element the #jump link pointed to
Stopping the browser default behavior is the part that is problematic for accessibility. No longer does the #jump link move focus to element the #jump link pointed to. So Heather added a #4: move focus to the element the #jump link pointed to.
But moving focus through JavaScript isn’t possible on every element. Sometimes you need to force that element to be focusable, which she did through setting tabindex="-1".
We since updated our snippet to do this more-accessible handling of focus. Since then, we’ve heard from quite a few folks about a new side effect to the updated snippet:
Notice the glowing blue outline of the headers that were scrolled to.
After the smooth scrolling, the headers that were scrolled to now have the default blue glowing outline that links have by default. Even if we don’t always love the blue glowing outline (the style varies by user agent, it might be a dotted outline, for example) we know better to remove it.
There is even a website dedicated to that sentiment:
You can change the focus style if you like, but it’s good accessibility to have a distinct focus style for elements that are in focus. I can even imagine an argument for leaving the focus styles alone.
But but but
Headers aren’t focusable elements.
They aren’t „interactive” elements. We aren’t used to seeing focus styles on non-interactive elements. But now, all the sudden, because we used a smooth scrolling technique from the internet, we’re getting them. That tabindex="-1" technique to allow focus is causing it.
I reached out to Heather to ask her about this.
The :focus styling for headings and other non-interactive elements can be removed/left off. It’s really up to the folks behind the creative site if they want it or not there. In some cases, I’ve removed it, in others I’ve styled it. It just depends.
To my surprise, it’s OK to remove focus styles in the case that the element you’re force-focusing isn’t interactive. That last point is important, and Heather clarified:
As long as the headings are not links… 😉
So if you really hated the focus styles, you could do:
You’re safe there. Even in the cases of <a><h3></h3></a> or <h3><a></a></h3>, the link still has focus styles.
It’s not just headers
Jump links can point to any element, and there are lots of non-focusable-by-default elements. Another classic technique in this situation is the „yellow fade” technique. Let’s say instead of linking to headers, we were linking to <section>s. When any section comes into focus, we could style it like this:
To no one’s surprise, I’m sure, there are lots of different ways to do the same thing on the web. Shape morphing, being a thing on the web, is no different. There are some native technologies, some libraries that leverage those, and some libraries that do things all on their own. Let’s look at some of the options (with demos) and weigh the advantages and disadvantages.
SMIL
The original, native technology for shape morphing is SMIL. We have both a guide to SMIL on CSS-Tricks, and an article talking about replacements for it, since it doesn’t work in Microsoft browsers and Blink threatened to yank it at one point.
I wouldn’t suggest doing important work in SMIL but it is OG shape morphing.
Moving right along to probably the most robust possible option, Greensock’s MorphSVG is a powerhouse. Bear in mind:
MorphSVGPlugin is a bonus plugin for Club GreenSock members („Shockingly Green” and „Business Green” levels). It’s our way of showing our gratitude to those who are fueling innovation at GreenSock.
Worth it. Just MorphSVG alone is amazing. Unlike almost any other shape morphing method, it can tween between shapes of any number of points. It does so performantly, in a safe cross-browser fashion, and gives you more fine grain control over how the animation goes down.
If you’d like to play with MorphSVG, I created a drag-and-drop Pen to morphing between any two shapes (best results with SVG’s with viewBox="0 0 100 100" SVGs with just one <path>):
It’s a little hard to believe, but CSS is getting in on the shape morphing action! Eric Willigers, a Chrome developer, emailed me last year:
’d’ has become a presentation attribute. This allows SVG path elements to be animated using CSS animations and Web Animations, with path(’…’) keyframes.
I assume this is a spec change, so browsers other than Chrome will, presumably, start allowing this. For now, this works great in Chrome:
The API gives you some control over the morphing, like how precise you want it to be and the ability to reverse the draw direction so different points match up to tween.
d3
d3, probably the biggest library out there for data viz folks, can also do shape morphing. Here’s a GIF recording of a demo by Mike Bostock:
There is also a d3 plugin called d3-interpolate-path that helps make it better:
Interpolates path `d` attribute smoothly when A and B have different number of points.
mo.js
„Motion graphics for the web” is the mo.js tagline, another newer library. Oleg Solomka (@LegoMushroom) has some incredible demos of what is is capable of:
If you happen to be a video person before you became a web person (or you’re still both) perhaps you have experience working in Adobe After Effects, which is all about creating „incredible motion graphics and visual effects”. The bodymovin library exports After Effects into SVG, including plenty of hot morphing action. Here’s a great demo:
If I had to blindly guess about global marketshare, I would have gotten it wrong. I probably would have forgotten about UC browser (kind of the point of Peter O’Shaughnessy’s article) that’s so huge in Asia. I would have guessed Firefox has a slight edge on Safari (turns out Firefox is half the share of Safari), and that Edge would be outpacing IE by now (also only half).
This is good dinner party conversation fodder, but I wouldn’t base any major decision making on it. The only stats that matter at your websites stats.
Here’s this sites, in the last 7 days.
There is always the chicken-or-egg conundrum though. If you support a browser that you didn’t before, doesn’t it stand to reason the numbers would go up for it? The time-on-site and bounce rate stuff would get better, anyway.
In CSS, some properties have shorthand. One property that takes separated values. Syntactic sugar, as they say, to make authoring easier. Take transition, which might look something like:
Dan Wilson recently used this kind of thing with animations to show how it’s possible to pause individual animations!
Here’s browser support:
Green indicates full support at the version listed (and above). Yellow indicates partial support. Red indicates no support. See Caniuse for full browser support details.
The other day I was watching an interview with Ade Oshineye where he discussed his work on the PWA Directory at Google, a showcase of progressive web apps. And it’s pretty neat!
It lists a whole bunch of PWAs out there and you can filter them by Lighthouse metrics – that’s the auditing tool from Google that scores a web app and gives us developers the ability to improve them.