Ahmad Shadeed nails it again with “Defensive CSS.” The idea is that you should write CSS to be ready for issues caused by dynamic content.
More items than you thought would be there? No problem, the area can expand or scroll. Title too long? No problem, it either wraps or truncates, and won’t bump into anything weird because margins or gaps are set up. Image come over in an unexpected size? No worries, the layout is designed to make sure the dedicated area is filled with image and will handle the sizing/cropping accordingly.
There is no such thing as being a good CSS developer and not coding defensively. This is what being a CSS developer is, especially when you factor in progressive enhancement concepts and cross-browser/device unknowns.
Many people believe that UX design is all about creating slick, engaging images and top-notch user flows. While those things have their merits, UX designers do much more than that.
UX writing is an essential skill in developing an app or solving a UX problem for a client. UX writing is when we craft UI text to communicate with a product/service user. UX writing includes valuable text like instructions, buttons, menus, just-in-time warnings, etc.
This article will analyze what you need to look out for even before you start writing. After that, we will explore ten crucial tips for UX writing that every UX designer should know.
Practical Tips and Tricks to Improve Your UX Writing
As with UX design in general, UX writing is about achieving a goal. Think of a wireframe you have created: The first thing you do when designing is to identify the real problem and set out to find the right solution. The same goes for UX writing.
Accordingly, before you start writing, make sure that you:
Have identified the UX problem;
Understand the goals of the client’s digital product;
Are familiar with the target audience;
Know the style and tone of voice you should use.
Once you are familiar with all of the above, you are ready to start writing. Let us explore all the tips and tricks you can follow to improve your UX writing.
1. Be Concise
This is one of the most important tips concerning UX writing. UX designers should always seek the shortest path from point A to point B. This is not only true for UX writing.
So conciseness is all about shortening your sentences and writing only what is necessary. This way, you’ll achieve a better user experience. Remember that most people tend to quickly scan instead of actually reading.
2. Be Clear and Helpful
If you are concise, the text you write will be clear and valuable. Since our goal is a compelling user experience, you should avoid being wordy. Our text needs to be helpful to the reader. What does a user need to know about the product or service?
All you need to do is anticipate what users need and what they are concerned about. Then, by analyzing your target audience, you can easily remove unnecessary text and make your UX text clear and useful.
3. Be Positive
You want the user to have a positive feeling when they first engage with your digital product. Well, for the target audience to be positive, your writing needs to be positive as well. To achieve that, you should avoid negative statements.
Of course, this is not a rule that you must always follow. For example, using “don’t” is not always bad because you can use it for emphasis. Nevertheless, try to avoid such words when describing your product or service.
4. Be Consistent
Consistency has everything to do with sticking to the guidelines you (or a client) set at the beginning of the UX project. Your UX text must always match the style and tone you have chosen.
Try to pay attention to details. For example, when it comes to numbers, you can write 2,000 follows, 2.000 follows, and 2000 follows. It does not matter which you choose, as long as you remain consistent throughout the project.
5. Use Active Voice
Although it is not always wrong to write in the passive voice, the active voice is generally more helpful for UX writing. Your text will be more precise, tailored to your audience, and more potent with the active voice. And when your written text is clear and powerful, it is also more engaging.
6. Don’t Get Wordy
In most cases, when you are done writing, you believe that your text is clear and valuable. And that’s reasonable. However, it is wise to reread your text and delete all the filler words.
Adjectives and adverbs are usually unnecessary, and you can remove them from your UX text.
7. Choose Strong Verbs
But if you have no adjectives and adverbs, how can you emphasize and strengthen your text? All you need to do is use the correct persuasive verb. Strong verbs help you formulate compelling CTAs without being wordy.
8. Use “You” Words
Now that we have mentioned CTAs, there is nothing more convincing than the word “you.” Words and phrases like “you,” “you will,” “you are,” “you have,” “your,” “yours,” etc., are the best way to connect with your target audience and let them be the leading character on your UX Journey Map.
9. Avoid Articles and Exclamation Points
Although it may sound bizarre, the use of articles is not necessary for people to understand what you are saying. The same goes for exclamation points. Of course, you can use exclamation points for emphasis, but not always. Save them for the most exciting aspects of your project.
10. Don’t Use UX Writing to Point Out UI
This is a widespread mistake made by us UX designers. If you have to point out an interface element of your design with text, there is probably something wrong with the design.
Remember, UX text is not about explaining your user interface; it’s about providing valuable and transparent information about your digital product.
Wrap Up
You should keep in mind that the above tips are only guidelines and not strict rules. For example, there are occasions when the passive voice or an exclamation point should be used in UX writing. In any case, you will be one step closer to your goal if you make sure that your UX text is concise, clear, valuable, and consistent.
I’m actually working on a talk (whew! been a while! kinda feels good!) about just how good the world of building websites has gotten. I plan to cover a wide swath of web tech, on purpose, because I feel like things have gotten good all around. CSS is doing great, but so is nearly everything else involved in making websites, especially if we take care in what we’re doing.
It also strikes me that updates to the web platform and the ecosystem around it are generally additive. If you feel like the web used to be simpler, well, perhaps it was—but it also still is. Whatever you could do then you can do now, if you want to, although, it would be a fair point if you’re job searching and the expectations to get hired involve a wheelbarrow of complicated tech.
This idea of the web getting better feels like it’s in the water a bit…
Simeon Griggs in “There’s never been a better time to build websites” has a totally different take on what is great on the web these days than mine, but I appreciate that. The options around building websites have also widened, meaning there are approaches to things that just feel better to people who think and work in different ways.
While there’s absolutely a learning curve to getting started, once you’ve got momentum, modern web development feels like having rocket boosters. The distance between idea and execution is as short as it’s ever been.
When you’re about to start a new website, what do you think first? Do you start with a library or framework you know, like React or Vue, or a meta-framework on top of that, like Next or Nuxt? Do you pull up a speedy build tool like Vite, or configure your webpack?
There’s a great tweet by Phil Hawksworth that I bookmarked a few years back and still love to this day:
Your websites start fast until you add too much to make them slow. Do you need any framework at all? Could you do what you want natively in the browser? Would doing it without a framework at all make your site lighter, or actually heavier in the long run as you create or optimize what others have already done?
I personally love the idea of shipping less code to ultimately ship more value to the browser. Understanding browser APIs and what comes “for free” could actually lead to less reinventing the wheel, and potentially more accessibility as you use the tools provided.
Instead of pulling in a library for every single task you want to do, try to look under the hood at what they are doing. For example, in a project I was maintaining, I noticed that we had a React component imported that was shipping an entire npm package for a small (less than 10-line) component with some CSS sprinkled on top (that we were overriding with our own design system). When we re-wrote that component from scratch, our bundle size was smaller, we were able to customize it more, and we didn’t have to work around someone else’s decisions.
Now, I’m not saying you shouldn’t use any libraries or frameworks or components out there. Open source exists for a reason! What I am saying is to be discerning about what you bring into your projects. Let the power of the browser work for you, and use less stuff!
High-velocity, online businesses produce multiple digital assets like banners, images, videos, PDFs, etc., to promote their businesses online. For such businesses, Digital Asset Management (DAM) solutions are essential. These solutions help centrally store, manage, organize, search and track digital assets. Having a central repository of assets helps in the faster execution of campaigns and improves cross-functional collaboration.
But, for an organization operating at scale and dealing with millions of digital assets flowing in from multiple sources, certain parts of your asset management workflow cannot be done manually using a UI. For example, how do you upload thousands of images in the correct folders every day? Or integrate an internal CMS to add product SKU IDs as a tag on the product image in the DAM?
This is why leading DAM solutions come with APIs to allow you to integrate them into your existing workflows and get the benefits of a DAM system at scale. Let’s first understand what an API is before getting to some common examples and use cases you can solve with them.
What is an API?
API stands for Application Programming Interface. It allows two software pieces or applications to communicate using a common definition.
An analogy in the physical world is when you order a dish in a restaurant, the chef understands what you ordered and prepares it. Here, the menu with the dish’s name serves as the common language for you (one of the parties) to communicate with the chef (the other party).
Let’s look at an example of an API in an e-commerce application. To check the delivery time to your location, you enter your pin code, and in a second or two, the time appears on your mobile screen. Here, your app (one of the software) is talking to the server (the other software), asking to give delivery times for a pin code (the definition or the common language between the two software). The delivery time that is returned by the server is called an API’s “response.”
What is a DAM API?
Continuing our explanation above, DAM APIs allow you to communicate with the Digital Asset Management system using a defined language. These APIs allow you to use all or most of the features of a DAM system, but instead of doing it via the user interface in a browser, you would be able to use them from a software program.
For example, a DAM’s user interface lets you drag and drop an image to upload it. However, the same DAM system could offer an API to upload images from your user’s Android app. Here the Android app is one of the software, the DAM system itself is the other software, and the upload API communicates what and how to upload to the DAM system. Once completed, the API responds with information about the uploaded image.
What’s ImageKit? What’s its DAM offering?
ImageKit is a leading Digital Asset Management solution. It comes with standard DAM features like storage, management, AI tagging, custom metadata, and advanced search. It also has optimized asset delivery integrated into the system.
While ImageKit’s DAM system comes with a user-friendly UI, like all leading players in this space, it also offers media APIs to use all of its features programmatically.
Use cases you can solve with DAM APIs
Before jumping to the APIs, here are some ways to use a DAM system’s APIs.
If you have an app or website where users can upload images or videos, or other content,you can use the DAM API to upload them directly to the DAM system.
Suppose you build a product that offers integrated media storage to its users. Instead of exposing your users to the DAM system directly, you would want to integrate it into your product natively (or white-label it). You can use a combination of DAM upload APIs, list and search APIs, and get image detail APIs to build this asset library for the users of your product.
Suppose your team uses an existing CMS or any other system to manage internal data. You can use the DAM as the underlying file storage and use its advanced management and search features via its APIs. Your team never has to leave their existing CMS while still leveraging all the features of the DAM system.
If you require and your DAM solution supports it, you can use the real-time image and video optimization APIs to deliver the assets to your users or on different platforms. ImageKit is one such DAM that supports file delivery for any asset upload to its media library.
Common Digital Asset Management APIs
Let’s look at some of the standard APIs that most DAM systems offer. For demonstration and examples, we would be using ImageKit’s DAM APIs.
1. API for uploading a file
This is the most basic API of all — before you use the DAM system, you need to upload files to it.
ImageKit’s Upload API allows you to upload an actual file from your file system or a web URL. You can use this API on a front-end application, like a mobile app, or a back-end application, like your application server. Here is an example of uploading the image from a back-end application.
curl -X POST "https://upload.imagekit.io/api/v1/files/upload"
-u your_private_api_key:
-F 'file=@/Users/username/Desktop/my_file_name.jpg;type=image/jpg'
-F 'fileName=my_file_name.jpg'
You would get some information about the uploaded file in the API response. For example, you would usually get a unique ID for your file, which would be super valuable for subsequent APIs, along with other information like the file’s format, size, upload time, etc.
After uploading a file to the DAM system, you might want to remove it or move it around to different folders. This also can be done programmatically via APIs.
For example, in ImageKit, to move a file from one folder to the other, you need to give the file’s path (sourceFilePath) and the destination folder path (destinationPath) in the API.
File nomenclature and creating the correct folder structure are often insufficient to organize and find content in a growing repository of digital assets.
Associating custom metadata or tags with an asset helps build another layer of organization for your content. For example, you could assign values to fields such as “Product Category” (Shoe, Shirt, Jeans, etc.), “Platform” (Facebook, Instagram, etc.), “Sale Name” (Thanksgiving, Black Friday, etc.) to the files in your DAM system, to build a more business-specific organization.
Through services like Google Cloud Vision, taking advantage of AI can help speed up asset tagging workflows and reduce errors. In addition, good DAM systems do provide APIs to associate tags with your assets.
For example, ImageKit allows you to add AI-inferred tags, using Google Cloud Vision, to your asset in the code below.
While the above API adds tags to an existing file, you can also do this when the file is first uploaded.
4. Searching for a file using search APIs
The most significant advantage of using a DAM is searching for the exact asset amongst thousands of them. Therefore, a good search API is necessary for any DAM system. It should allow searching on all the possible parameters associated with an asset, including custom tags and metadata that we add to create a business-specific organization for ourselves.
ImageKit provides a very flexible search API that lets you construct complex search queries to pinpoint the exact resource you need. The example below finds out all assets you created more than seven days ago with a size of more than 2MB.
curl -X GET "https://api.imagekit.io/v1/files"
-G --data-urlencode "searchQuery=createdAt >= "7d" AND size > "2mb""
-u your_private_api_key:
5. The image and video delivery API
Once your team starts managing and collaborating on the assets on the DAM, the next obvious step would be to be able to use these assets on the web, share them via their URLs, use them on your website, apps, emails, and so on.
Leading DAM solutions like ImageKit provide ready-to-use URLs for any file stored with them. ImageKit API also has in-built automatic optimizations and real-time manipulations for images and videos that ensure optimized asset delivery every time.
The above example resizes the original image to a 200×200 square thumbnail while compressing it and optimizing its format. But, of course, you can do the same using a similar URL-based API for videos too.Read more about ImageKit’s media APIs
Conclusion
Apart from the basic APIs explained above, all DAM solutions offer several other APIs that allow you to manage folders, get file details, control the shareability of assets, and more. The possibilities are endless for integrating these APIs to simplify and automate your existing workflows. Using a DAM solution like ImageKit, with its extensive media management APIs given here, will bring your marketing, creative, and technology teams on the same page and help them execute campaigns faster. Sign up today on ImageKit’s forever free DAM plan and start optimizing your media workflows.
Animation on the web is often a contentious topic. I think, in part, it’s because bad animation is blindingly obvious, whereas well-executed animation fades seamlessly into the background. When handled well, animation can really elevate a website, whether it’s just adding a bit of personality or providing visual hints and lessening cognitive load. Unfortunately, it often feels like there are two camps, accessibility vs. animation. This is such a shame because we can have it all! All it requires is a little consideration.
Here’s a couple of important questions to ask when you’re creating animations.
Does this animation serve a purpose?
This sounds serious, but don’t worry — the site’s purpose is key. If you’re building a personal portfolio, go wild! However, if someone’s trying to file a tax return, whimsical loading animations aren’t likely to be well-received. On the other hand, an animated progress bar could be a nice touch while providing visual feedback on the user’s action.
Is it diverting focus from important information?
It’s all too easy to get caught up in the excitement of whizzing things around, but remember that the web is primarily an information system. When people are trying to read, animating text or looping animations that play nearby can be hugely distracting, especially for people with ADD or ADHD. Great animation aids focus; it doesn’t disrupt it.
So! Your animation’s passed the test, what next? Here are a few thoughts…
Did we allow users to opt-out?
It’s important that our animations are safe for people with motion sensitivities. Those with vestibular (inner ear) disorders can experience dizziness, headaches, or even nausea from animated content.
Luckily, we can tap into operating system settings with the prefers-reduced-motion media query. This media query detects whether the user has requested the operating system to minimize the amount of animation or motion it uses.
This snippet taps into that user setting and, if enabled, it gets rid of all your CSS animations and transitions. It’s a bit of a sledgehammer approach though — remember, the key word in this media query is reduced. Make sure functionality isn’t breaking and that users aren’t losing important context by opting out of the animation. I prefer tailoring reduced motion options for those users. Think simple opacity fades instead of zooming or panning effects.
What about JavaScript, though?
Glad you asked! We can make use of the reduced motion media query in JavaScript land, too!
Tapping into system preferences isn’t bulletproof. After all, it’s there’s no guarantee that everyone affected by motion knows how to change their settings. To be extra safe, it’s possible to add a reduced motion toggle in the UI and put the power back in the user’s hands to decide. We {the collective} has a really nice implementation on their site
Here’s a straightforward example:
CodePen Embed Fallback
Scroll animations
One of my favorite things about animating on the web is hooking into user interactions. It opens up a world of creative possibilities and really allows you to engage with visitors. But it’s important to remember that not all interactions are opt-in — some (like scrolling) are inherently tied to how someone navigates around your site.
The Nielson Norman Group has done some great research on scroll interactions. One particular part really stuck out for me. They found that a lot of task-focused users couldn’t tell the difference between slow load times and scroll-triggered entrance animations. All they noticed was a frustrating delay in the interface’s response time. I can relate to this; it’s annoying when you’re trying to scan a website for some information and you have to wait for the page to slowly ease and fade into view.
If you’re using GreenSock’s ScrollTrigger plugin for your animations, you’re in luck. We’ve added a cool little property to help avoid this frustration: fastScrollEnd.
fastScrollEnd detects the users’ scroll velocity. ScrollTrigger skips the entrance animations to their end state when the user scrolls super fast, like they’re in a hurry. Check it out!
CodePen Embed Fallback
There’s also a super easy way to make your scroll animations reduced-motion-friendly with ScrollTrigger.matchMedia():
CodePen Embed Fallback
I hope these snippets and insights help. Remember, consider the purpose, lead with empathy, and use your animation powers responsibly!
Lea Verou made a Web Component for processing Markdown. Looks like there were a couple of others out there already, but I agree with Lea in that this is a good use case for the light DOM (as opposed to the shadow DOM that is normally quite useful for web components), and that’s what Lea’s does. The output is HTML so I can imagine it’s ideal you can style it on the page like any other type rather than have to deal with that shadow DOM. I still feel like the styling stories for shadow DOM all kinda suck.
The story of how it came to be is funny and highly relatable. You just want to build one simple thing and it turns out you have to do 15 other things and it takes the better part of a week.
One of the best things you can do for your website in 2022 is add a service worker, if you don’t have one in place already. Service workers give your website super powers. Today, I want to show you some of the amazing things that they can do, and give you a paint-by-numbers boilerplate that you can use to start using them on your site right away.
What are service workers?
A service worker is a special type of JavaScript file that acts like middleware for your site. Any request that comes from the site, and any response it gets back, first goes through the service worker file. Service workers also have access to a special cache where they can save responses and assets locally.
Together, these features allow you to…
Serve frequently accessed assets from your local cache instead of the network, reducing data usage and improving performance.
Provide access to critical information (or even your entire site or app) when the visitor goes offline.
Prefetch important assets and API responses so they’re ready when the user needs them.
Provide fallback assets in response to HTTP errors.
In short, service workers allow you to build faster and more resilient web experiences.
Unlike regular JavaScript files, service workers do not have access to the DOM. They also run on their own thread, and as a result, don’t block other JavaScript from running. Service workers are designed to be fully asynchronous.
Security
Because service workers intercept every request and response for your site or app, they have some important security limitations.
Service workers follow a same-origin policy.
You can’t run your service worker from a CDN or third party. It has to be hosted at the same domain as where it will be run.
Service workers only work on sites with an installed SSL certificate.
Many web hosts provide SSL certificates at no cost or for a small fee. If you’re comfortable with the command line, you can also install one for free using Let’s Encrypt.
There is an exception to the SSL certificate requirement for localhost testing, but you can’t run your service worker from the file:// protocol. You need to have a local server running.
Adding a service worker to your site or web app
To use a service worker, the first thing we need to do is register it with the browser. You can register a service worker using the navigator.serviceWorker.register() method. Pass in the path to the service worker file as an argument.
navigator.serviceWorker.register('sw.js');
You can run this in an external JavaScript file, but prefer to run it directly in a script element inline in my HTML so that it runs as soon as possible.
Unlike other types of JavaScript files, service workers only work for the directory in which they exist (and any of its sub-directories). A service worker file located at /js/sw.js would only work for files in the /js directory. As a result, you should place your service worker file inside the root directory of your site.
While service workers have fantastic browser support, it’s a good idea to make sure the browser supports them before running your registration script.
if (navigator && navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js');
}
After the service worker installs, the browser can activate it. Typically, this only happens when…
there is no service worker currently active, or
the user refreshes the page.
The service worker won’t run or intercept requests until it’s activated.
Listening for requests in a service worker
Once the service worker is active, it can start intercepting requests and running other tasks. We can listen for requests with self.addEventListener() and the fetch event.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Do stuff...
});
Inside the event listener, the event.request property is the request object itself. For ease, we can save it to the request variable.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Get the request
let request = event.request;
// Bug fix
// https://stackoverflow.com/a/49719964
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
});
Once your service worker is active, every single request is sent through it, and will be intercepted with the fetch event.
Service worker strategies
Once your service worker is installed and activated, you can intercept requests and responses, and handle them in various ways. There are two primary strategies you can use in your service worker:
Network-first. With a network-first approach, you pass along requests to the network. If the request isn’t found, or there’s no network connectivity, you then look for the request in the service worker cache.
Offline-first. With an offline-first approach, you check for a requested asset in the service worker cache first. If it’s not found, you send the request to the network.
Network-first and offline-first approaches work in tandem. You will likely mix-and-match approaches depending on the type of asset being requested.
Offline-first is great for large assets that don’t change very often: CSS, JavaScript, images, and fonts. Network-first is a better fit for frequently updated assets like HTML and API requests.
Strategies for caching assets
How do you get assets into your browser’s cache? You’ll typically use two different approaches, depending on the types of assets.
Pre-cache on install. Every site and web app has a set of core assets that are used on almost every page: CSS, JavaScript, a logo, favicon, and fonts. You can pre-cache these during the install event, and serve them using an offline-first approach whenever they’re requested.
Cache as you browser. Your site or app likely has assets that won’t be accessed on every visit or by every visitor; things like blog posts and images that go with articles. For these assets, you may want to cache them in real-time as the visitor accesses them.
You can then serve those cached assets, either by default or as a fallback, depending on your approach.
Implementing network-first and offline-first strategies in your service worker
Inside a fetch event in your service worker, the request.headers.get('Accept') method returns the MIME type for the content. We can use that to determine what type of file the request is for. MDN has a list of common files and their MIME types. For example, HTML files have a MIME type of text/html.
We can pass the type of file we’re looking for into the String.includes() method as an argument, and use if statements to respond in different ways based on the file type.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Get the request
let request = event.request;
// Bug fix
// https://stackoverflow.com/a/49719964
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
// HTML files
// Network-first
if (request.headers.get('Accept').includes('text/html')) {
// Handle HTML files...
return;
}
// CSS & JavaScript
// Offline-first
if (request.headers.get('Accept').includes('text/css') || request.headers.get('Accept').includes('text/javascript')) {
// Handle CSS and JavaScript files...
return;
}
// Images
// Offline-first
if (request.headers.get('Accept').includes('image')) {
// Handle images...
}
});
Network-first
Inside each if statement, we use the event.respondWith() method to modify the response that’s sent back to the browser.
For assets that use a network-first approach, we use the fetch() method, passing in the request, to pass through the request for the HTML file. If it returns successfully, we’ll return the response in our callback function. This is the same behavior as not having a service worker at all.
If there’s an error, we can use Promise.catch() to modify the response instead of showing the default browser error message. We can use the caches.match() method to look for that page, and return it instead of the network response.
// Send the request to the network first
// If it's not found, look in the cache
event.respondWith(
fetch(request).then(function (response) {
return response;
}).catch(function (error) {
return caches.match(request).then(function (response) {
return response;
});
})
);
Offline-first
For assets that use an offline-first approach, we’ll first check inside the browser cache using the caches.match() method. If a match is found, we’ll return it. Otherwise, we’ll use the fetch() method to pass the request along to the network.
// Check the cache first
// If it's not found, send the request to the network
event.respondWith(
caches.match(request).then(function (response) {
return response || fetch(request).then(function (response) {
return response;
});
})
);
Pre-caching core assets
Inside an install event listener in the service worker, we can use the caches.open() method to open a service worker cache. We pass in the name we want to use for the cache, app, as an argument.
The cache is scoped and restricted to your domain. Other sites can’t access it, and if they have a cache with the same name the contents are kept entirely separate.
The caches.open() method returns a Promise. If a cache already exists with this name, the Promise will resolve with it. If not, it will create the cache first, then resolve.
// Listen for the install event
self.addEventListener('install', function (event) {
event.waitUntil(caches.open('app'));
});
Next, we can chain a then() method to our caches.open() method with a callback function.
In order to add files to the cache, we need to request them, which we can do with the new Request() constructor. We can use the cache.add() method to add the file to the service worker cache. Then, we return the cache object.
We want the install event to wait until we’ve cached our file before completing, so let’s wrap our code in the event.waitUntil() method:
// Listen for the install event
self.addEventListener('install', function (event) {
// Cache the offline.html page
event.waitUntil(caches.open('app').then(function (cache) {
cache.add(new Request('offline.html'));
return cache;
}));
});
I find it helpful to create an array with the paths to all of my core files. Then, inside the install event listener, after I open my cache, I can loop through each item and add it.
let coreAssets = [
'/css/main.css',
'/js/main.js',
'/img/logo.svg',
'/img/favicon.ico'
];
// On install, cache some stuff
self.addEventListener('install', function (event) {
// Cache core assets
event.waitUntil(caches.open('app').then(function (cache) {
for (let asset of coreAssets) {
cache.add(new Request(asset));
}
return cache;
}));
});
Cache as you browse
Your site or app likely has assets that won’t be accessed on every visit or by every visitor; things like blog posts and images that go with articles. For these assets, you may want to cache them in real-time as the visitor accesses them. On subsequent visits, you can load them directly from cache (with an offline-first approach) or serve them as a fallback if the network fails (using a network-first approach).
When a fetch() method returns a successful response, we can use the Response.clone() method to create a copy of it.
Next, we can use the caches.open() method to open our cache. Then, we’ll use the cache.put() method to save the copied response to the cache, passing in the request and copy of the response as arguments. Because this is an asynchronous function, we’ll wrap our code in the event.waitUntil() method. This prevents the event from ending before we’ve saved our copy to cache. Once the copy is saved, we can return the response as normal.
/explanation We use cache.put() instead of cache.add() because we already have a response. Using cache.add() would make another network call.
// HTML files
// Network-first
if (request.headers.get('Accept').includes('text/html')) {
event.respondWith(
fetch(request).then(function (response) {
// Create a copy of the response and save it to the cache
let copy = response.clone();
event.waitUntil(caches.open('app').then(function (cache) {
return cache.put(request, copy);
}));
// Return the response
return response;
}).catch(function (error) {
return caches.match(request).then(function (response) {
return response;
});
})
);
}
If you do nothing else, this will be a huge boost to your site in 2022.
But there’s so much more you can do with service workers. There are advanced caching strategies for APIs. You can provide an offline page with critical information if a visitor loses their network connection. You can clean up bloated caches as the user browses.
Jeremy Keith’s book, Going Offline, is a great primer on service workers. If you want to take things to the next level and dig into progressive web apps, Jason Grigsby’s book dives into the various strategies you can use.
And for a pragmatic deep dive you can complete in about an hour, I also have a course and ebook on service workers with lots of code examples and a project you can work on.
I hadn’t heard of most of the Chrome extensions that Sarem Gizaw lists as 2021 favorites. Here are my hot takes on all of them, except the virtual learning specific ones that aren’t very relevant to me.
LoomOh that’s neat how it records your screen and your camera, encouraging you to do little personal walkthroughs of a thing for someone. I actually had a guest author use this to pitch an idea and it was both above-and-beyond in a good way, and didn’t look overly difficult to do. I like how it’s a browser app, meaning that something like a Chromebook could use it even though it feels like an app that would otherwise be native.
MoteSimilar in spirit to Loom—leave feedback with your voice instead of writing it out by hand. This is clearly a much more personal way to do things. I wonder if it’s big in the eduction space where that student/teacher connection would be bolstered by this.
WordtuneLooks like a competitor to Grammarly. I like to see that sort of competition. That said, I think Grammarly is good, though a bit heavy handed sometimes. It’s nice to see them being pushed by another player in the same space.
ForestI’m skeptical of productivity apps that ultimately feel like yet-another-app I need to use and learn. But maybe it’s worth it if the point is that it keeps you on task and away from distracting apps? I was very skeptical of Centered, but it really did seem to help the times I tried it.
Dark ReaderThis forces dark mode on sites that otherwise don’t have one. I’ve heard it does a pretty decent job.
Tab Manager PlusI’m as guilty as anyone for having too many tabs open… all the time. But now that Chrome has tab groups natively (as well as “pinning”), I just roll with that.
NimbusInteresting that this is in an entirely different category that Loom. I guess it’s more about screenshots than video. Note that you don’t need a browser plugin to do full-length screenshots—in DevTools you can do CMD+Shift+P then search for “Capture Full Size Screenshot.” I actually use that quite a bit. For other screenshots, I’m super hot on Cleanshot.
StylusI love that this exists and that it is still somewhat actively developed (a beta exists). Being able to slap some extra CSS onto sites of your own liking and have it persist is super cool, and a community of user-created styles for other websites is even cooler.
RakutenThis is one of those things that automatically applies coupons during checkout to eCommerce stores. Skeeves me out for some reason, especially the “Cash Back” idea. I would think that if they are sending you money, it’s because they are making money off of you, meaning it’s from hidden-feeling affiliate partnerships or by selling your purchase data and personal details.
Browser extensions have come a long way toward being cross-browser compatible, so I’d think a lot of these are available for Safari and Firefox now—or at least could be without enormous amounts of work if the authors felt like doing it.
Notably, there are no ad blocker plugins in the list. Not a huge surprise there, even though I’m sure they are some of the most-downloaded and used. I use Ghostery, but I haven’t re-evaluated the landscape there in a while. I like how Ghostery makes it easy for me to toggle on-and-off individual scripts, both on individual sites and broadly across all sites. That means I could enable BuySellAds (something even Adblock Plus does by default) and Google Analytics scripts, but turn off A/B testers or gross ad networks.
With a new year here, it’s time to try out some new fonts.
Whether you’re designing a brand new website or redesigning an existing one, the following list of fonts has you covered. In addition to the dependable serifs and sans serifs we use to create attractive and readable content, this roundup also has some fun additions, including one you can use for websites advertising Valentine’s Day deals next month.
Antona
Antona is a geometric sans serif font family with 16 different styles. The solid structures and ample white space within the characters give off a safe and friendly vibe.
Aromanis
Aromanis is a small font family with just two variations: Regular and Shadow. This new font supports nearly 70 languages and has an extensive Latin character set with localized forms. This font works best in branding for youthful companies with a playful vibe — from logos to posters and everything in between.
Black Coopy
Black Coopy is an edgy display typeface that would work well for sporty brands. In addition to the standard alpha, numeric, and punctuation sets, the font also comes with a variety of “swash” characters that can be used to frame your bold headlines.
Cimory Love
Don’t wait until February to start thinking about how to infuse a little romance into your designs. Cimory Love is a script font that comes in two styles: Regular and Italic. In addition to using it to promote Valentine’s Day sales, this could also be a cute font to use on websites for small gift shops, bakeries, and so on.
Cotford
Cotford is a contemporary serif font with a ton of flexibility built into it. It comes with eight variations — three text and five display weights. Designers can use one of the many pre-designed styles or they can modify this dynamic font set to make it suit their specific needs.
Digno
Digno is a beautiful, informal serif font that’s easy on the eyes. The font family comes with 14 weights covering a wide spectrum — lights, mediums, heavies, and even a couple of “Book” weights are thrown in if you want to add some personality to those text-dense pages of yours.
Dogly Comika
Dogly Comika is a rounded display font with two styles: Regular and outline. While it’s promoted as a font for animals and pets, you could use it for any type of website hero image, mobile app splash screen, video game, or social media graphic for brands with a fun vibe.
Guzzo
Guzzo is a nostalgic typeface inspired by mid-century grotesques. With 24 styles ranging from Condensed Thin to Extended Black and unexpected character variations (like the random cursives in the italics), you could realistically create interesting font pairings right from within this family.
Idem
Idem is a contemporary serif with nine wide-ranging styles that would work well for headers and text alike. Inspired by literary publications and commercial artists from the earlier part of the 20th century, this font family has a highly legible structure with a bold flare.
Jantur Type
Jantur Type is a geometric sans font that supports over 200 Latin-based languages. While you could use one of the Thin or Regular weights for editorial content, this font will be most effective in shorter headers and paragraphs where it can make a greater impact on messaging.
Loretta
Loretta is an elegant serif designed specifically for the body of your web pages. Because of its calligraphic roots, this particular font would work great for high-end digital publications or blogs that promote luxury lifestyles and goods.
Rebrand
Rebrand is an exciting take on geometric sans. There are two sub-families in Rebrand: Display comes with nine weights as well as alternative characters and dingbats; Text comes with seven weights that cover a broad spectrum of styles. Because of the size and variety of this font family, you could easily make this the go-to font for a company’s branding, headers, and body type.
Royal Grotesque
Royal Grotesque is a resurrection of a 1914 sans serif font called Wotan. Only one version of this font is available (Regular) and it would work great pretty much anywhere on the web with its clean and neutral design.
Selva
Selva is an attractive Scotch typeface that has a traditional Roman serif family, an italicized version of each Roman, as well as a script family. If you’re considering using a script font for branding or headlines, the classic and delicate details of this particular font would make for an interesting choice.
Sunset Gothic
Sunset Gothic is a sans serif inspired by signage found near and around Los Angeles. Because this signage was often painted directly onto shop windows and building facades, the letterforms had to be extremely legible for passersby and drivers alike. This font draws upon the hand-painted, vector-based styling of those painted promotions.