Crows, Ghosts, And Autumn Bliss (October 2024 Wallpapers Edition)

The leaves are shining in the most beautiful colors and pumpkins are taking over the front porches. It’s time to welcome the spookiest of all months: October! To get your desktop ready for fall and the upcoming Halloween season, artists and designers from across the globe once again got their ideas flowing and designed inspiring wallpapers for you to indulge in.

The wallpapers in this post come in versions with and without a calendar for October 2024 and can be downloaded for free. And since so many beautiful and unique designs evolve around our every month (we’ve been running it for more than 13 years already, can you believe it?!), we also added some timeless October treasures from our to the collection. Maybe you’ll spot one of your almost-forgotten favorites in here, too?

A huge thank you to everyone who shared their wallpapers with us this month — this post wouldn’t exist without you. Happy October!

  • You can click on every image to see a larger preview,
  • We respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience through their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

  • Did you know that you could get featured in our next wallpapers post, too? We are always looking for creative talent.

Happy Halloween

Designed by from Spain.

  • with calendar: , , , , , , , , , , , , , , , , , , , ,
  • without calendar: , , , , , , , , , , , , , , , , , , , ,

Reptile Awareness Day

“Let’s celebrate reptiles and raise awareness of their vital role in ecosystems. Many species face threats, so let’s learn, appreciate, and protect these incredible creatures and their habitats!” — Designed by from Serbia.

  • with calendar: , , , , , , , , , , , , , , , , , , ,
  • without calendar: , , , , , , , , , , , , , , , , , , ,

Make Today A Good Day

“‘Make today a good day’ is a simple yet powerful reminder to take control of the present moment. It emphasizes that our attitude and actions shape our experience, encouraging positivity and purpose. Each day brings new opportunities, and by choosing to make it good, we invite growth, joy, and fulfilment into our lives.” — Designed by from Delhi, India.

  • with calendar: , , , , , , , , , , , ,
  • without calendar: , , , , , , , , , , , ,

The Dungeon Master

Designed by from Spain.

  • with calendar: , , , , , , , , , , , , , , , , , , , ,
  • without calendar: , , , , , , , , , , , , , , , , , , , ,

Happy Dussehra

“I was inspired by Dussehra’s rich symbolism and cultural significance while creating this design. The festival celebrates the triumph of good over evil. The bow and arrow become the central focus, while the bold red background, golden accents, and the temple’s silhouette add a sense of grandeur and spirituality.” — Designed by from the United States.

  • with calendar: , , , , , , , , , , , , , , , , , , , , , , , , ,
  • without calendar: , , , , , , , , , , , , , , , , , , , , , , , , ,

The Crow And The Ghosts

“If my heart were a season, it would be autumn.” — Designed by from Hungary.

  • without calendar: , , , , , , , , ,

The Night Drive

Designed by from Georgia.

  • without calendar: , , , , , , , , , , , , , , , , , , , , , , , , ,

Autumn’s Splendor

“The transition to autumn brings forth a rich visual tapestry of warm colors and falling leaves, making it a natural choice for a wallpaper theme.” — Designed by from India.

  • without calendar: , , , , , , , , , ,

National Fossil Day

“Join us in commemorating National Fossil Day, a day dedicated to honoring the wonders of Earth’s prehistoric past. On this special day, we invite you to step back in time and explore the remarkable world of fossils. These ancient remnants of life on our planet offer a glimpse into the evolution of life, from the tiniest microorganisms to the towering giants that once roamed the Earth.” — Designed by from Serbia.

  • without calendar: , , , , , , , , , , , , , , , , , , ,

Magical October

“‘I’m so glad I live in a world where there are Octobers.’ (L. M. Montgomery, Anne of Green Gables)” — Designed by from Hungary.

  • without calendar: , , , , , , , , ,

Bird Migration Portal

“October is a significant month for me because it is when my favorite type of bird travels south. For that reason I have chosen to write about the swallow. When I was young, I had a bird’s nest not so far from my room window. I watched the birds almost every day; because those swallows always left their nests in October. As a child, I dreamt that they all flew together to a nicer place, where they were not so cold.” — Designed by from Belgium.

  • without calendar: , , , , , , , ,

Ghostbusters

Designed by from Spain.

  • without calendar: , , , , , , , , , , , , , , , , , , , ,

Spooky Town

Designed by from Germany.

  • without calendar: , , , , , , , , , , , , , , , , , , ,

Hello Autumn

“Did you know that squirrels don’t just eat nuts? They really like to eat fruit, too. Since apples are the seasonal fruit of October, I decided to combine both things into a beautiful image.” — Designed by Erin Troch from Belgium.

  • without calendar: , , , , , , , , ,

Hanlu

“The term ‘Hanlu’ literally translates as ‘Cold Dew.’ The cold dew brings brisk mornings and evenings. Eventually the briskness will turn cold, as winter is coming soon. And chrysanthemum is the iconic flower of Cold Dew.” — Designed by Hong, ZI-Qing from Taiwan.

  • without calendar: , , , , , , , , , , , ,

Discovering The Universe

“Autumn is the best moment for discovering the universe. I am looking for a new galaxy or maybe… a UFO!” — Designed by from Spain.

  • without calendar: , , , , , , , ,

King Of The Pirates

Designed by from Spain.

  • without calendar: , , , , , , , , , , , , , , , , , ,

Goddess Makosh

“At the end of the kolodar, as everything begins to ripen, the village sets out to harvesting. Together with the farmers goes Makosh, the Goddess of fields and crops, ensuring a prosperous harvest. What she gave her life and health all year round is now mature and rich, thus, as a sign of gratitude, the girls bring her bread and wine. The beautiful game of the goddess makes the hard harvest easier, while the song of the farmer permeates the field.” — Designed by from Serbia.

  • without calendar: , , , , , , , , , , , , , , , , , , , ,

Game Night And Hot Chocolate

“To me, October is all about cozy evenings with hot chocolate, freshly baked cookies, and a game night with friends or family.” — Designed by Lieselot Geirnaert from Belgium.

  • without calendar: , , ,

Strange October Journey

“October makes the leaves fall to cover the land with lovely auburn colors and brings out all types of weird with them.” — Designed by Mi Ni Studio from Serbia.

  • without calendar: , , , , , , , , , , , , , , , , , , ,

Autumn Deer

Designed by from Canada.

  • without calendar: , , , , , , , , , , , , ,

Dope Code

“October is the month when the weather in Poland starts to get colder, and it gets very rainy, too. You can’t always spend your free time outside, so it’s the perfect opportunity to get some hot coffee and work on your next cool web project!” — Designed by Robert Brodziak from Poland.

  • without calendar: , , , , , , , , , , , , , , ,

Transitions

“To me, October is a transitional month. We gradually slide from summer to autumn. That’s why I chose to use a lot of gradients. I also wanted to work with simple shapes, because I think of October as the ‘back to nature/back to basics month’.” — Designed by Jelle Denturck from Belgium.

  • without calendar: , , , , , , ,

Autumn In The Forest

“Autumn is a wonderful time to go for walks in the forest!” — Designed by from Sweden.

  • without calendar: , , , , , ,

Shades Of Gold

“We are about to experience the magical imagery of nature, with all the yellows, ochers, oranges, and reds coming our way this fall. With all the subtle sunrises and the burning sunsets before us, we feel so joyful that we are going to shout it out to the world from the top of the mountains.” — Designed by from Serbia.

  • without calendar: , , , , , , , , , , , , , , , , , , , ,

Happy Fall!

“Fall is my favorite season!” — Designed by from the United States.

  • without calendar: , , , , , , , , , , , , , ,

Ghostober

Designed by from Mexico City.

  • without calendar: , , , ,

First Scarf And The Beach

“When I was little, my parents always took me and my sister for a walk at the beach in Nieuwpoort. We didn’t really do those beach walks in the summer but always when the sky started to turn gray and the days became colder. My sister and I always took out our warmest scarfs and played in the sand while my parents walked behind us. I really loved those Saturday or Sunday mornings where we were all together. I think October (when it’s not raining) is the perfect month to go to the beach for ‘uitwaaien’ (to blow out), to walk in the wind and take a break and clear your head, relieve the stress or forget one’s problems.” — Designed by from Belgium.

  • without calendar: , , ,

Turtles In Space

“Finished September, with October comes the month of routines. This year we share it with turtles that explore space.” — Designed by from Spain.

  • without calendar: , , , , , , , , ,

Roger That Rogue Rover

“The story is a mash-up of retro science fiction and zombie infection. What would happen if a Mars rover came into contact with an unknown Martian material and got infected with a virus? What if it reversed its intended purpose of research and exploration? Instead choosing a life of chaos and evil. What if they all ran rogue on Mars? Would humans ever dare to voyage to the red planet?” Designed by from the United States.

  • without calendar: , , , , , ,

Summer, Don’t Go!

“It would be nice if we could bring summer back, wouldn’t it?” — Designed by from Serbia.

  • without calendar: , , , , , , , , , , , , , , , , , , ,

Embracing Autumn’s Beauty

“We were inspired by the breathtaking beauty of autumn, with its colorful foliage and the symbolic pumpkin, which epitomizes the season. Incorporating typography allows us to blend aesthetics and functionality, making the calendar not only visually appealing but also useful.” — Designed by from India.

  • without calendar: , , , , , , , , , ,

A Positive Fall

“October is the month when fall truly begins, and many people feel tired and depressed in this season. The jumping fox wants you to be happy! Also, foxes always have reminded me of fall because of their beautiful fur colors.” — Designed by Elena Sanchez from Spain.

  • without calendar: , , , , , , , , , , , , , , , , , , ,

How To Manage Dangerous Actions In User Interfaces

By definition, an interface is a layer between the user and a system, serving the purpose of communication between them. Interacting with the interface usually requires users to perform certain actions.

Different actions can lead to various outcomes, some of which might be critical.

While we often need to provide additional protection in case users attempt to perform dangerous or irreversible actions, It’s good to remember that one of called “Error Prevention” says:

“Good error messages are important, but the best designs carefully prevent problems from occurring in the first place. Either eliminate error-prone conditions or check for them and present users with a confirmation option before they commit to the action.”

What Is A Dangerous Action?

Surprisingly, when we talk about dangerous actions, it doesn’t necessarily mean that something is being deleted.

Here’s an example of a dangerous action from the banking application I use:

The bank approved a loan for me, and as soon as I clicked “Get Money,” it meant that I had signed the necessary documents and accepted the loan. All I have to do is tap the yellow button, and I’ll get the money.

As a result of an accidental tap, you might end up taking a loan when you didn’t intend to, which is why this action can be considered significant and dangerous.

Therefore, a dangerous action does not necessarily mean deleting something.

Some examples may include the following:

  • Sending an email,
  • Placing an order,
  • Publishing a post,
  • Making a bank transaction,
  • Signing a legal document,
  • Permanently blocking a user,
  • Granting or revoking permissions.

Ways To Confirm Dangerous Actions

There are many methods to prevent users from losing their data or taking irreversible actions unintentionally. One approach is to ask users to explicitly confirm their actions.

There are several ways to implement this, each with its own pros and cons.

Modal Dialogs

First of all, we should understand the difference between modal and non-modal dialogs. It’s better to think about modality state since dialogs, popups, alerts — all of these might be presented either in the modal state or not. I will use the term dialogs as a general reference, but the keyword here is modality.

“Modality is a design technique that presents content in a separate, dedicated mode that prevents interaction with the parent view and requires an explicit action to dismiss.”

Modal dialogs require immediate user action. In other words, you cannot continue working with an application until you respond in some way.

Non-modal dialogs, on the other hand, allow you to keep using the application without interruption. A common example of a non-modal element is a toast message that appears in the corner of the screen and does not require you to do anything to continue using the app.

When used properly, modal dialogs are an effective way to prevent accidental clicks on dangerous actions.

The main problem with them is that if they are used to confirm routine actions (such as marking a task as done), they can cause irritation and create a habit of mindlessly confirming them on autopilot.

However, this is one of the most popular methods. Besides, it can be combined with other methods, so let’s dive into it deeper.

When To Use Them

Use modal dialogs when a user action will have serious consequences, especially if the result of the action is irreversible. Typical cases include deleting a post or project, confirming a transaction, and so on.

It depends on what kind of action users want to take, but the main thing to keep in mind is how serious the consequences are and whether the action is reversible or not.

Things To Keep In Mind

  1. Avoid vague language.
    If you ask users, “Are you sure?” chances are, they will not have any doubts.
  2. In the title, specify what exactly will happen or which entity will be affected (e.g., project name, user name, amount of money).
  3. Provide an icon that indicates that the action is dangerous.
    It both increases the chances that users will not automatically confirm it and is good for accessibility reasons (people with color blindness will notice the icon even if it appears grey to them, signaling its importance).
  4. In the description, be specific and highlight the necessary information.
  5. The CTA button should also contain a word that reflects the action.
    Instead of “Yes” or “Confirm,” use more descriptive options like “Delete,” “Pay $97,” “Make Transaction,” “Send Message,” and so on — including the entity name or amount of money in the button is also helpful. Compare: “Confirm” versus “Pay $97.” The latter is much more specific.

However, this might not be enough.

In some cases, you may require an extra action. A typical solution is to ask users to type something (e.g., a project name) to unblock the CTA button.

Here are a few examples:

asks users to type “DO IT” when removing subscribers.

Pro tip: Note that they placed the buttons on the left side! This is a nice example of applying . It seems reasonable since the submit button is closer to the form (even if it consists of only one input).

asks users to type “DELETE” if they want to delete an API key, which could have very serious consequences. The API key might be used in many of your apps, and you don’t want to break anything.

This modal is one of the best examples of following the best practices:

  • The title says what the action is (“Delete API Key”).
  • In the text, they mentioned the name of the API Key in bold and in a different color (“Onboarding”).
  • The red label that the action can not be undone makes it clearer that this is a serious action.
  • Extra action is required (typing “DELETE”).
  • The CTA button has both a color indicator (red usually is used for destructive actions) and a proper label — “Delete API Key”. Not a general word, e.g., “Confirm” or “Delete.”

Notice that Resend also places buttons on the left side, just as ConvertKit does.

Note: While generally , this is one of the cases where it is acceptable. The dialog’s request is clear and straightforward both in ConvertKit and Resend examples.

Moreover, we can even skip the submit button altogether. This applies to cases where users are asked to input an OTP, PIN, or 2FA code. For example, the bank app I use does not even have a log in button.

On the one hand, we still ask users to perform an extra action (input the code). On the other hand, it eliminates the need for an additional click.

Accessibility Concerns

There is ongoing debate about whether or not to include a submit button when entering a simple OTP. By “simple,” I mean one that consists of 4-6 digits.

While I am not an accessibility expert, I don’t see any major downsides to omitting the submit button in straightforward cases like this.

First, the OTP step is typically an intermediate part of the user flow, meaning a form with four inputs appears during some process. The first input is automatically focused, and users can navigate through them using the Tab key.

The key point is that, due to the small amount of information required (four digits), it is generally acceptable to auto-submit the form as soon as the digits are entered, even if a mistake is made.

On the one hand, if we care about accessibility, nothing stops us from providing users control over the inputs. On the other hand, auto-submission streamlines the process in most cases, and in the rare event of an error, the user can easily re-enter the digits.

Danger Zones

For the most critical actions, you may use the so-called “Danger zone” pattern.

A common way to implement this is to either have a dedicated page or place the set of actions at the bottom of the settings/account page.

It might contain one or more actions and is usually combined with other methods, e.g., a modal dialog. The more actions you have, the more likely you’ll need a dedicated page.

When To Use Them

Use a Danger Zone to group actions that are irreversible or have a high potential for data loss or significant outcomes for users.

These actions typically include things like account deletion, data wiping, or permission changes that could affect the user’s access or data.

Things To Keep In Mind

  1. Use colors like red, warning icons, or borders to visually differentiate the Danger Zone from the rest of the page.
  2. Each action in the Danger Zone should have a clear description of what will happen if the user proceeds so that users understand the potential consequences.
  3. Ask users for extra effort. Usually, the actions are irreversible and critical. In this case, you may ask users to repeat their password or use 2FA because if someone else gets access to the page, it will not be that easy to do the harmful action.
  4. Keep only truly critical actions there. Avoid making a danger zone for the sake of having one.

Inline Guards

Recently, I discovered that some apps have started using inline confirmation. This means that when you click on a dangerous action, it changes its label and asks you to click again.

This pattern is used by apps like Zapier and Typefully. While at first it seems convenient, it has sparked a lot of discussion and questions on and .

I’ve seen attempts to try to fix accidental double-clicking by changing the position of the inline confirmation label that appears after the first click.

But this creates layout shifts. When users work with the app daily, it may cause more irritation than help.

As an option, we can solve this issue by adding a tiny delay, e.g., 100-200ms, to prevent double-clicking.

It also matters who your users are. Remember the good old days when we used to click a dozen times to launch Internet Explorer and ended up with dozens of open instances?

If your target audience is likely to do this, apparently, the pattern will not work.

However, for apps like Zapier or Typefully, my assumption is that the target audience might benefit from the pattern.

Two-factor Authorization Confirmation

This method involves sending a confirmation request, with or without some kind of verification code, to another place, such as:

  • SMS,
  • Email,
  • Authenticator app on mobile,
  • Push notifications (e.g., instead of sending SMS, you may choose to send push notifications),
  • Messengers.

Notice: I’m not talking about authentication (namely, login process), but rather a confirmation action.

An example that I personally face a lot is an app for sending cryptocurrency. Since this is a sensitive request, apart from submitting the requisition from a website, I should also approve it via email.

When To Use It

It can be used for such operations as money transfers, ownership transfers, and account deletion (even if you have a danger zone). Most of us use this method quite often when we pay online, and our banks send us OTP (one-time password or one-time code).

It may go after the first initial protection method, e.g., a confirmation dialog.

As you can see, the methods are often combined and used together. We should not consider each of them in isolation but rather in the context of the whole business process.

Passkeys

Passkeys are a modern, password-less authentication method designed to enhance both security and user experience.

“Passkeys are a replacement for passwords. A password is something that can be remembered and typed, and a passkey is a secret stored on one’s devices, unlocked with biometrics.”

There are a few pros of using passkeys over 2FA, both in terms of security and UX:

  1. Unlike 2FA, which typically requires entering a code from another device or app (e.g., SMS or authenticator apps), passkeys streamline the confirmation process. They don’t require switching between devices or waiting for a code to arrive, providing immediate authentication.
  2. While 2FA provides extra protection, it is vulnerable to phishing, SIM-swapping, or interception. Passkeys are much more resistant to such attacks because they use public-private key cryptography. This means no secret code is ever sent over the network, making it phishing-resistant and not reliant on SMS or email, which can be compromised.
  3. Passkeys require less mental effort from users. There’s no need to remember a password or type a code — just authenticate with a fingerprint, facial recognition, or device-specific PIN. This way, we .
  4. With passkeys, the authentication process is almost instant. Unlike 2FA, where users might have to wait for a code or switch to another device, passkeys give us the opportunity to confirm actions without switching context, e.g., opening your email inbox or copying OTP from a mobile device.

The passkeys are and more and more adopt it.

Second-person Confirmation

This is a mechanism when two users are involved in the process. We may call them initiator and approver.

In this case, the initiator makes a request to take some action while the approver decides whether to confirm it or not.

In both roles, a confirmation dialog or other UI patterns may be used. However, the main idea is to separate responsibilities and decrease the probability of a bad decision.

Actually, you have likely encountered this method many times before. For example, a developer submits a pull request, while a code reviewer decides whether to confirm it or decline.

When To Use It

It is best suited for situations when the seriousness of decisions requires few people involved.

There is a direct analogy from real life. Take a look at the picture below:

The Council of Physicians reminds us that in medicine, seeking a second opinion is crucial, as collaboration and diverse perspectives often result in more informed decisions and better patient care. This is a perfect example of when a second opinion or an approver is essential.

Here, you will find some apps that use this method:

  • GitHub, as previously mentioned, for merging pull requests.
  • Jira and other similar apps. For example, when you move issues through a certain workflow stage, it may require manager approval.
  • Banking applications. When you make a high-value transaction, it could be necessary to verify it for legal issues.
  • Deel, which is a global hiring and payroll. One part (e.g., employer) draws up a contract and sends it to another part (e.g., freelancer), and the freelancer accepts it.

But here is the thing: We can consider it a separate method or rather an approach for implementing business logic because even if another person confirms an action, it is still a dangerous action, with the only difference being that now it’s another person who should approve it.

So, all of the examples mentioned above are not exactly a standalone specific way to protect users from making wrong decisions from the UI point of view. It’s rather an approach that helps us to reduce the number of critical mistakes.

Do We Actually Need To Ask Users?

When you ask users to take action, you should be aware of its original purpose.

The fact that users make actions does not mean that they make them consciously.

There are many behavioral phenomena that come from psychology, to name a few:

  • : The tendency of a person to stick to familiar decisions, even if they are not suitable for the current situation. For instance, the vast majority of people don’t read user agreements. They simply agree with the lengthy text because it’s necessary from the legal point of view.
  • : People often make decisions based on information that is easily accessible or familiar to them rather than making a mental effort. When users see the same confirmation popups, they might automatically accept them based on their previous successful experience. Of course, sooner or later, it might not work, and the acceptance of required action can lead to bad consequences.
  • : The human mind is considered to be a cognitive miser due to the tendency of humans to think and solve problems in simpler and less effortful ways rather than in more sophisticated and effortful ways, regardless of intelligence. This explains why many users just click “yes” or “agree” without carefully reading the text.
  • Quite a representative example is , even though not related to confirmation but, in fact, revolves around the same human behavior idiosyncrasies.

A reasonable question that may arise: What are the alternatives?

Even though we cannot entirely affect users’ behavior, there are a few tactics we can use.

Delaying

In some scenarios, we can artificially delay the task execution in a graceful way.

One of my favorite examples is an app called Glovo, which is a food delivery app. Let’s have a look at the three screens you will see when you order something.

The first screen is a cart with items you chose to buy (and an annoying promotion of subscription that takes ⅓ of the screen).

After you tap the “confirm order” button, you’ll see the second screen, which asks you whether everything is correct. However, the information appears gradually with fade-in animation. Also, you can see there is a progress bar, which is a fake one.

After a few seconds, you’ll see another screen that shows that the app is trying to charge your card; this time, it’s a real process. After the transaction proceeds, you’ll see the status of the order and approximate delivery time.

Pro tip: When you show the status of the order and visually highlight or animate the first step, it makes users more confident that the order will be completed. Because of the trick that is called .

You’ve just paid, and “something starts happening” (at least visually), which is a sign that “Oh, they should have already started preparing my order. That’s nice!”

The purpose of the screen with a fake progress bar is to let users verify the order details and confirm them.

But this is done in a very exquisite way:

  1. On the first screen, you click “confirm order”. It doesn’t invoke any modals or popups, such as “Are you sure?”.
  2. On the second screen, users can see how information about their order appears right away, and the scroll bar at the bottom goes further. It seems like that app is doing something, but it’s an illusion. An illusion that makes you take another quick look at what you’ve just ordered.

In the previous version of the app, you couldn’t even skip the process; you could only cancel it. Now they added the “Continue” button, which is essentially “Yes, I’m sure” confirmation.

This means that we return back again to the drawbacks of classic confirmation modals since users can skip the process. But the approach is different: it’s a combination of a feedback loop from the app and skipping the process.

This combination makes users pay attention to the address, order, and price at least sometimes, and it gives them time to cancel the order, while in the classic approach, the confirmation is “yes or no?” which is more likely to be confirmed right away.

The Undo Option

The undo pattern allows users to reverse an action they have just performed, providing a safety net that reduces anxiety around making mistakes.

Unlike confirmation modals that interrupt the workflow to ask for user confirmation, the undo pattern provides a smoother experience by allowing actions to be completed with the option to reverse them if needed.

When To Use It

It works perfectly fine for non-destructive, reversible actions &mdashl actions that don’t have significant and immediate consequences:

  • Reversing actions when editing a document (The beloved ctrl + z shortcut);
  • Removing a file (if it goes to the trash bin first);
  • Changing the status of a task (e.g., if you accidentally marked a task completed);
  • Deleting a message in a chat;
  • Applying filters to a photo.

Combined with a timer, you can extend the number of options since such tasks as sending an email or making a money transfer could be undone.

When You Cannot Use It

It’s not suitable for actions that have serious consequences, such as the following:

  • Deleting an account;
  • Submitting legal documents;
  • Purchasing goods (refund is not the same as the undo option);
  • Making requests for third-party APIs (in most cases).

How To Implement Them?

  1. The most common way that most people use every day is to provide a shortcut (ctrl + z). However, it’s constrained to some cases, such as text editors, moving files between folders, and so on.
  2. Toasts are probably the most common way to implement these web and mobile apps. The only thing that you should keep in mind is that it should stand out enough to be noticed. Hiding them in a corner with a tiny message and color that is not noticeable might not work — especially on wide screens.
  3. A straightforward solution is simply to have a button that does the undo option. Preferably close to the button that evokes the action that you want to undo.

The undo option is tightly related to the concept called soft deleting, which is widely used in backend frameworks such as .

The concept means that when users delete something via the UI, it looks like it has been deleted, but in the database, we keep the data but mark it as deleted. The data is not lost, which is why the undo option is possible since we don’t actually delete anything but rather mark it as deleted.

This is a good technique to ensure that data is never lost. However, not every table needs this.

For example, if you delete an account and don’t want users to restore it (perhaps due to legal regulations), then you should erase the data completely. But in many cases, it might be a good idea to consider soft deleting. In the worst case, you’ll be able to manually restore user data if it cannot be done via the UI for some reason.

Conclusion

There’s something I want everyone to keep in mind, regardless of who you are or what you do.

Every situation is unique. A certain approach might work or fail for a variety of reasons. You might sometimes wonder why a specific decision was made, but you may not realize how many times the interface was revised based on real user feedback.

User behavior is affected by many factors, including country, age, culture, education, familiarity with certain patterns, disabilities, and more.

What’s crucial is to stay in control of your data and users and be prepared to respond when something goes wrong. Following best practices is important, but you must still verify if they work in your specific case.

Just like in chess, there are many rules — and even more exceptions.

Further Reading

  • , Apple
  • “,” João Bexiga
  • “,” Josh Wayne
  • “,” Suraj Singh Bisht
  • “,” Jakob Nielsen
  • “,” Kinneret Yifrah
  • , passkeys.io

The Timeless Power Of Spreadsheets

Part of me can’t believe I’m writing this article. Applying the insights of or to web design is more my groove, but sometimes you simply have to write about spreadsheets. You have to advocate for them. Because someone should.

In a checkered career spanning copywriting, journalism, engineering, and teaching, I’ve seen time and time again how powerful and useful spreadsheets are in all walks of life. The cold, hard truth is that you — yes, you — likely have an enormous amount to gain by understanding how spreadsheets work. And, more importantly, how they can work for you.

That’s what this piece is about. It’s a rallying cry, with examples of spreadsheets’ myriad uses and how they can actually, in the right circumstances, be the bedrock of altogether inspiring, lovely things.

Cellular Organisms

Spreadsheets have been around for thousands of years. . Their going digital in the late ‘70s was a major factor in the rise of personal computing. Much is (rightly) made of the cultural transformation brought about by the printing press. The digital spreadsheet, not so much.

For as long as people have had projects and data to organize, spreadsheets have been indispensable. They were the original databases.

Spreadsheets don’t always get a lot of attention these days. For organization and workflow, we usually find ourselves in the worlds of Trello, Jira, or GitHub Projects. Datasets live in Oracle, MongoDB, and the like. There are good reasons for these services emerging — everything has its place — but

I do get the sense that specialized tooling causes us to skip over the flexibility and power that today’s spreadsheet editors provide.

This is especially true for smaller projects and ones in their early stages. Yes, sometimes only a huge database will do, but often spreadsheets are more than fit for purpose.

Benefits

What makes spreadsheets so great? We’ll get into a few real-world examples in a second, but several qualities hold true. They include the following:

  • Collaboration
    Cloud-based editors like Google Sheets give groups of people a space in which to collaborate on data. They can serve as a middle ground for people working on different parts of the same project.
  • Structure
    It’s inherent to spreadsheets that they’ll get you thinking about the ‘shape’ of the information you’re dealing with. In the same way that , tables coax out frameworks — and both have their place
  • Flexibility
    Spreadsheets can evolve in real time, which is especially useful during the formative stages of a project when the shape of the ‘data’ is still being established. Adding a field is as simple as naming a column, and the ability to weave in formulas makes it easy to infer other values from the ones you have. With stuff like the , you can even scrape data directly from the spreadsheet
  • Power
    You’d be surprised how much you can do in spreadsheets. Sometimes, you don’t even need bespoke dashboards; you can do it all in the editor. From data visualization to pivot tables, spreadsheet editors come with a bunch of powerful out-of-the-box features.
  • They translate into other data formats
    Spreadsheets are one small jump from the mighty CSV. When the time is right, spreadsheets can still become raw data if you want them to.

Such is the flexibility and power of spreadsheets, and what’s listed here is scratching the surface. Their fundamental strength of organizing data has made them useful for thousands of years, while contemporary enhancements have taken them to the next level.

Case Studies

Below are a few examples from my own experiences that showcase these benefits in the real world. They’re obviously slanted towards my interests, but hopefully, they illustrate the usefulness of spreadsheets in different contexts.

Galaxies (Of The Guardian)

I work as a software engineer at Guardian News & Media, a place where 10% of the time, i.e., one work day every two weeks, is yours to spend on independent learning, side projects, and so on, is part of the working culture. An ongoing project of mine has been Galaxies (of the Guardian), a org chart that represents departments as a series of interrelated people, teams, and streams.

What you see above is powered by information stored and edited in spreadsheets. A lambda scraps departmental information using the aforementioned Google Sheets API, then reformats into a shape Galaxies plays nicely with.

This approach has had several benefits. The earliest iterations of Galaxies were only possible because there was already a spreadsheet being maintained by those who needed to keep track of who worked where. Techies and non-techies alike are able to update information easily, and it is transparent to anyone who works inside the organization.

For anyone interested, . Suffice it to say here, spreadsheets were — and remain — the engine of the whole thing.

Food Bank Britain

My background is in journalism, and I still freelance in my own time. As my coding skills have improved, I’ve naturally gravitated towards data journalism, even teaching it for a year at my old journalism school.

Spreadsheets are inseparable from a lot of modern journalism — and, indeed, copyrighting in general. The digital world is awash with data, and good luck making sense of it without a working knowledge of spreadsheets.

For example, earlier this year simply wouldn’t have been possible without spreadsheets. It was by collating data from the Trussell Trust, the Independent Food Aid Network, and national census reports that I was able to map out the sheer scale of the UK’s food bank network.

Granted, the map is more visually engaging. But then that’s the idea. It’s the same information, just presented more pointedly.

There are plenty of other instances of spreadsheets being instrumental at the Guardian alone. , the newspaper’s automated house style checker, began life as a subeditor’s spreadsheet. User research and bug tracking for the new , which I worked on during its formative stages, was tracked and discussed in spreadsheets.

And, of course, countless pieces of quality journalism at the Guardian and beyond continue to be powered by them.

Another Cell In The Table

If this piece has got you to at least consider learning more about spreadsheets and spreadsheet editors, you’re in luck. There are countless free learning resources available on the web. Here are a few excellent beginner videos to help you on your way:

As for spreadsheet editors, the big three these days are probably , , and (for the open source devotees out there). They all work much the same way. And as you get comfortable with their functionality, new avenues will open.

Data is the lifeblood of the modern web, and spreadsheets remain one of the most accessible, flexible ways to organize, analyze, and share it. As I hope the examples I’ve shared with you show, spreadsheets aren’t inherently boring. They can be, but when used in the right ways, they become the engines of dynamic, impactful work.

The way they go is up to you.

Embracing Introversion In UX

I place myself firmly in the category of being an introvert when it comes to my role as a UX researcher. I love the process of planning and executing research. I have never felt a need to be the loudest or most talkative person in a meeting. I contribute after I have developed something worth saying (or have a really bad joke worked up).

I also love interviews and usability testing, where I interact with users and engage in meaningful conversation. And then I am exhausted. I love speaking about the findings of research and sharing the spotlight with my colleagues during a presentation, and then I want to go to bed underneath the conference room table. I facilitate workshops with ease but have trouble mustering up the energy required to attend what often feels like mandatory post-workshop socializing.

In truth, I have sometimes felt introverted tendencies set me back at work, particularly as a consultant who needs to build relationships to keep the work flowing (in theory). An example would be getting called out by a manager in my junior days for not engaging in as many networking activities as I could have been with some of our clients. My defense of feeling overstimulated, overwhelmed, and uninterested in socializing fell on deaf ears.

I think we have grown in our understanding of introverts and what they need to be high performers, particularly since Susan Cain’s 2013 best-selling book was released.

This article aims to celebrate the power of introversion in UX research and design. We’ll debunk common misconceptions, explore the unique strengths introverted researchers and designers bring to the table, and offer practical tips for thriving in a field that sometimes seems tailored for extroverts. My goal is to build on some of the work on UX and introversion that already exists. I’ve cited other articles where appropriate and shared the resources I’ve found on UX and introversion at the end of this article.

Introversion is not the same thing as being shy, just as extroversion isn’t the same thing as being brash. For simplicity and the sake of this article, I am going to use the following definitions provided by :

“Extroverts get energy from interaction with others and like to share ideas with others to help develop their thinking, whereas introverts need to recharge on their own after much social contact and prefer to share ideas only when they are fully formed.”

There are many potential reasons one could have introvert or extrovert tendencies (, and these come on a scale where one might lean or introvert or extrovert depending on the occasion. Those who straddle the middle ground of introversion and extroversion are considered ambiverts.

As Jonathan Walter notes in a series of , many UX professionals find themselves drawn to the field because of their introverted nature. Introversion, often misunderstood as shyness or social awkwardness, is simply a preference for internal reflection and processing. It’s about drawing energy from solitude and finding fulfillment in deep thought and meaningful connections.

As UX is clearly a space where introverts are drawn, there is already a decent amount of literature aimed at introverted UX practitioners. In writing this article, I wanted to differentiate from what is already out there, as well as extend.

I wanted to include some personal stories of introverts who aren’t myself and work in UX. To do this, I went to LinkedIn and asked people to send me personal anecdotes. My post, at least by my standards, was well received, with over 100 reactions and a dozen people sending me direct messages sharing anecdotes. I was even introduced to Tim Yeo, who has recently released a . I’ll be sharing some of the stories people shared with me over LinkedIn, where appropriate (and with their permission), throughout this article to help draw the connections to real life.

First, let’s talk a little about what we know about measuring if you (or others) are introverted, extroverted, or in between.

Measuring Introversion & Extroversion: Self-Assessment Tools

Understanding where you and your team members fall on the introversion-extroversion spectrum can be invaluable for tailoring your approach to work, collaboration, and personal development. , two medical school professors, write that medical educators should know where they fall on the introversion — extroversion spectrum to deliver great teaching experiences. I’d extend this to UX practitioners, including UX managers, UX researchers, and designers. If we collaborate with others, we will benefit from knowing where we fall on this scale.

While there’s no single definitive test, here are a few simple and accessible tools that can offer insights:

  1. Online Quizzes: Numerous online quizzes and assessments are available, often based on established personality frameworks like the Myers-Briggs Type Indicator (MBTI) or the Big Five personality traits. These quizzes can provide a general sense of your tendencies and preferences. Popular options include:
    • : Offers a free, comprehensive assessment based on the MBTI.
    • : Provides a variety of personality tests, including the Big Five and Enneagram.
    • : Offers a quiz specifically focused on introversion and extroversion.
  2. Reflection and Journaling: Take some time to reflect on your daily experiences and interactions. Ask yourself the following questions:
    • What activities energize me vs. drain me?
    • Do I prefer to work alone or in groups?
    • How do I recharge after a long day?
    • Do I prefer deep conversations with a few people or socializing with a larger group?
  3. Observation: Pay attention to your behavior and reactions in different social settings. Notice what triggers your stress response and what environments make you feel most comfortable and productive.
  4. Professional Assessment: If you’re seeking a more in-depth analysis, consider consulting a career coach or psychologist who specializes in personality assessment. They can administer standardized tests and provide personalized feedback and guidance.
    • Multidimensional Introversion-Extroversion Scales (MIES): This scale specifically focuses on the multifaceted nature of introversion and extroversion. It measures several sub-traits associated with each dimension, such as social engagement, assertiveness, enjoyment of social interaction, and preference for solitude. Professional psychologists often reference this test, which can be accessed freely , but might be best done with the guidance of a professional.

There’s no right or wrong answer when it comes to introversion or extroversion. You might even find some folks are ambiverts who display different personalities in different settings. You can’t force your teammates to take these types of tests. But if you are able to get buy-in, it can be a fun activity to see who considers themselves more introverted or more extroverted. The goal is to understand your own preferences and tendencies and those of your colleagues so you can create a work environment that supports your well-being and maximizes your potential.

Introverts’ Super Powers

The idea that UX is an extrovert’s game couldn’t be further from the truth. As Jeremy Bird notes in his article on the , it’s a field that demands a wide range of skills, including deep listening, empathy, observation, analysis, and creativity — all of which introverts excel at. With so much information already available from articles on UX and introversion noted in the biography below, I’m going to briefly highlight the commonly accepted strengths of introverts.

Deep Listening

Introverts are often exceptional listeners. In user interviews, they give participants the space to fully express their thoughts and feelings, picking up on subtle cues and nuances that others might miss. This active listening leads to a deeper understanding of user needs and motivations, which is crucial for both research and design.

One practitioner shared their experience on LinkedIn:

“In a nutshell, being introverted gives a natural advantage in giving the user space to tell their story. I’m more likely to embrace pauses that others may feel are awkward, but this allows users to either double down on their point or think of another point to add (“lightbulb” moment).”

— Dominique S. Microsoft User Research via LinkedIn

Empathy

Many introverts possess a high degree of empathy. They can easily put themselves in users’ shoes, feeling their frustrations and celebrating their successes. This empathy fuels user-centered design, ensuring that products and services are not only functional but also emotionally resonant.

Observational Skills

Introverts are naturally observant. They notice details in user behavior, interface interactions, and environmental context that others might overlook.

Thoughtful Analysis

Introverts often prefer to process information internally, engaging in deep, solitary reflection before sharing their insights. This leads to well-considered and insightful findings and well-crafted data-informed design.

Independent Work

Introverts tend to thrive in independent work environments. As notes, teachers should allow introverted students to work independently or in pairs. This way, they can focus deeply on research tasks, design problems, or data analysis without the distractions that come with constant collaboration.

Now that we’ve covered the commonly recognized strengths introverts bring to the table, let’s cover some common hurdles and explore effective strategies for overcoming them that empower introverts to thrive.

Potential Challenges (And How To Overcome Them)

Being introverted can bring up some challenges when it comes to doing things that require a lot of social energy. However, many introverts in UX find ways to push beyond their natural tendencies to meet the demands of their profession. One UX practitioner shared their experience on LinkedIn:

“I’ve been extremely introverted all my life, but have always been able to proceed beyond my introverted boundaries because of a commitment to (perceived) duty. My passion for synergizing user needs, business needs, and the assorted bevy of constraints that arise helps me downplay and overlook any challenges arising from my tendency to be withdrawn.”

— Darren H. MS UXD via LinkedIn

Networking

Introverts might initially feel overwhelmed in networking situations or workshops due to the continual social interaction and the need to navigate unfamiliar environments and interact with new people, which can be particularly daunting for those who prefer solitude or small-group conversations.

  • Researchers & Designers: Building professional relationships can be challenging for introverts. Large conferences or networking events can feel overwhelming. Small talk can feel forced and inauthentic.
    • Solutions for researchers and designers:
      • Focus on quality over quantity: Instead of trying to meet as many people as possible, focus on building a few meaningful connections.
      • Utilize online communities: Connect with other UX professionals on platforms like LinkedIn or Twitter. Engage in discussions, share your insights, and build relationships virtually before meeting in person.
      • Attend smaller events: Look for niche conferences or meetups focused on specific areas of interest. These tend to be more intimate and less overwhelming than large-scale events.
      • Leverage existing relationships: Don’t be afraid to ask a colleague or mentor to introduce you to someone new.

Presenting Work and Public Speaking

Introverts may initially avoid presenting because they tend to prefer avoiding the spotlight. They may also worry about being judged or scrutinized by others.

  • Researchers: May feel anxious about presenting research findings to stakeholders, especially if they have to do so in front of a large audience.
  • Designers: Can struggle with pitching design concepts or justifying their decisions to clients or colleagues, fearing criticism or pushback.

For the introvert, you might not like this, but you need to get comfortable presenting, and the sooner you do, the better.

Solutions for researchers and designers:

  • Practice, practice, practice
    The more you rehearse your presentation or pitch, the more comfortable you’ll feel. Practice in front of a mirror, record yourself or ask a trusted friend for feedback.
  • Use visual aids
    Slides, mockups, or prototypes can help you illustrate your points and keep your audience engaged.
  • Focus on clear communication
    Structure your presentation logically, use simple language, and avoid jargon. Speak slowly and confidently, and make eye contact with your audience.
  • Build confidence over time
    Start with small presentations or informal feedback sessions. As you gain experience and positive feedback, your confidence will naturally increase.

I’ve personally found presenting in front of a large anonymous crowd to be less intimidating than smaller, intimate meetings where you might know a few people mixed in with a few strangers. In the end, I always remind myself I am supposed to be the expert on what I’ve been asked to present or that my job is to clearly state the outcome of our research to stakeholders hungry to see the relevance of their work. The audience wants to support you and see you succeed. I take confidence in that. I’m also exhausted after giving a presentation where I’ve left it all on the floor.

Now, let’s move on to topics beyond what I’ve found covered in existing articles on UX and introversion and cover workshop facilitation and managing group dynamics.

Managing Group Dynamics

Introverts may find group dynamics challenging, as they often prefer solitary activities and may feel overwhelmed or drained by social interactions. In group settings, introverts may have difficulty asserting themselves, sharing their ideas, or actively participating in discussions. They may also feel uncomfortable being the center of attention or having to make decisions on the spot.

Additionally, introverts may struggle to build relationships with their peers in a group setting, as they may be hesitant to initiate conversations or join in on group activities. These challenges can make it difficult for introverts to fully engage and contribute in group settings, leading to feelings of isolation and exclusion.

One UX designer responding over LinkedIn eloquently shared their experience with communication challenges:

“Introversion can sometimes create challenges in communication, as my thoughtful nature can be misinterpreted as shyness or disinterest. To step out of my shell, I need to build trust with those around me before I can feel truly comfortable. However, I don’t see this as the worst thing in the world. Instead, I view it as an opportunity to identify areas where I need to improve and learn to advocate for myself more effectively in the future. In embracing both the strengths and challenges of being an introvert, I’ve found that my introverted nature not only enhances my work as a designer but also drives continuous personal and professional growth, ultimately leading to better outcomes for both myself and my team.”

— Arafa A. via LinkedIn

  • Challenge: Large groups can be overwhelming, and introverted facilitators might find it difficult to assert control or manage dominant personalities who may derail the discussion.
  • Solutions:
    • Clear Ground Rules: Establish explicit ground rules at the beginning of the workshop to ensure respectful communication and equal participation.
    • Assertive Communication: Practice techniques like “broken record” or “fogging” to politely but firmly redirect the conversation when necessary.
    • Partner with a Co-Facilitator: Collaborate with an extroverted colleague who can complement your strengths. They can take the lead in managing group dynamics and energizing participants.

Managing group dynamics covers a broad number of situations UX professionals face on a daily basis. Let’s get a little more specific and focus on how introverted UXers can thrive as workshop facilitators.

Facilitating Workshops

If you’re an introverted UX professional who shies away from leading workshops, it’s time to reconsider. Here are some of the reasons introverts can be perfect workshop facilitators:

  1. Preparation:
    • Introverts tend to be meticulous planners. We thrive on preparation and often go above and beyond to ensure a workshop is well-structured, organized, and aligned with learning objectives. This thoroughness translates to a smooth, well-paced session that instills confidence in participants.
  2. Thoughtful Facilitation:
    • Introverts are known for their active listening skills. We genuinely want to hear what others have to say and create a safe space for diverse perspectives to emerge. We ask thoughtful questions, encourage reflection, and facilitate meaningful discussions that lead to deeper understanding.
  3. Empathy & Connection: We’ve already discussed in the section on superpowers how introverts excel at empathy and connection.
  4. Observation Skills: We’ve already discussed in the section on superpowers how introverts excel at observational skills.
  5. Comfort with Silence:
    • Introverts understand the power of silence. We’re not afraid to pause and allow reflection after asking a question or during a brainstorming session. This creates space for deeper thinking and prevents premature conclusions or groupthink.

We’ve reviewed many of the challenges introverts might face in their daily work life. Let’s turn our attention to a more recent phenomenon, at least in terms of its widespread availability as an option for many UX professionals: remote work.

Working Remotely

Increased telecommuting offers a unique opportunity for some introverts. Introverts, who often find comfort in solitude and derive energy from spending time alone, sometimes find the constant socialization and bustle of open-plan offices overwhelming and draining.

Remote work provides introverts with an opportunity to control their surroundings and create a workspace that promotes focus, productivity, and creativity. Remote work allows introverts to communicate and collaborate on their own terms. Introverts often prefer one-on-one interactions over large group meetings, and remote work makes it easier for them to engage in meaningful conversations with colleagues and clients.

Potential Challenges For Introverts Working Remotely

While remote work has been a game-changer for many introverts, it is important to acknowledge that it is not without its challenges. Introverts may miss the camaraderie and social connections of an in-person workplace, and they may need to make a conscious effort to stay connected with colleagues and maintain a healthy work-life balance.

Introverts working remotely may need to develop strategies for self-advocacy and communication to ensure that their voices are heard and their contributions are valued in a virtual work environment.

  • Isolation and Disconnect: The lack of face-to-face interaction can lead to feelings of isolation and detachment from the team.
  • Communication Barriers: Virtual communication can be less nuanced, making it harder to convey complex ideas or build rapport with colleagues.
  • Meeting Overload: Excessive video calls can be exhausting for introverts, leading to burnout and decreased productivity.
  • Limited Non-Verbal Cues: Virtual interactions lack the subtle body language and facial expressions that introverts rely on to understand others’ perspectives.

Overcoming Challenges: Strategies For Introverts Working Remotely

Introverted remote employees can implement some of these strategies and tactics to enhance their productivity, reduce burnout, and maintain a positive work environment:

  • Proactive Communication: Initiate regular check-ins with colleagues and managers, both for work-related updates and casual conversations.
  • Schedule Breaks: During long virtual meetings, take short breaks to recharge and refocus.
  • Advocate for Your Needs: If you’re feeling overwhelmed by meetings or social interactions, don’t hesitate to speak up and suggest alternatives, such as asynchronous communication or smaller group discussions.
  • Build Virtual Relationships: Participate in virtual social events, share personal anecdotes in team channels, and find opportunities to connect with colleagues on a personal level.
  • Embrace Video Calls (Strategically): While video calls can be tiring, they can also be valuable for building rapport and understanding non-verbal cues. Use them strategically for important discussions or when you need to connect with a colleague on a deeper level.

Implementing what we’ve covered in this section will help to reduce the likelihood of frustration from both remote working introverts and their colleagues.

Tips For Introverted UX Researchers And Designers

We’ve covered a lot of ideas in this article. If you find yourself nodding along as an introvert or perhaps coming to the realization you or someone on your team is more introverted, this section and the next will end this article on a high note, introducing some actionable tips for introverted researchers and designers, and their managers and teammates, to create a more comfortable and successful working environment for introverts to thrive alongside their extroverted colleagues.

Self-Care

Everyone needs to engage in an appropriate amount of self-care to feel their best. For an introvert, this is often done in solitude, particularly after engaging in a day or week full of social interaction. Some tips that could apply to anyone but are of particular relevance to introverts include the following:

  • Schedule downtime: Block out time in your calendar for quiet reflection and recharging after meetings or social interactions. This could be a walk in nature, reading a book, or simply sitting in silence.
  • Honor your energy levels: Pay attention to when you’re feeling drained. Don’t be afraid to decline invitations or reschedule meetings if you need time to recharge.
  • Create a calming workspace: Surround yourself with things that promote relaxation and focus, such as plants, calming music, or inspiring artwork.

Play To Your Strengths

Introverts know themselves best and have spent a lifetime reflecting on who they are and what makes them wake up happy to go to work. As such, introverts may have a high awareness of their strengths. This allows an introvert to do the following:

  • Identify your unique talents: Are you a meticulous researcher, a creative problem-solver, or a passionate user advocate? Focus on tasks and projects that align with your strengths.
  • Communicate your preferences: Let your manager or team know what type of work you thrive in. Perhaps you prefer to work independently on research tasks or focus on the visual aspects of design.
  • Build on your skills: Seek opportunities to develop your existing skills and acquire new ones. This could involve taking online courses, attending workshops, or seeking mentorship from experienced researchers and designers.

Communication

Introverts might hesitate to speak up when the room is crowded with unknown future friends. However, anyone, introverted or not, needs to be their own best advocate when it comes to making colleagues and management aware of how to create the best workplace environment to thrive in:

  • Advocate for your needs: Don’t be afraid to speak up and ask for what you need to succeed. This could involve requesting a quiet workspace, suggesting alternative meeting formats, or simply letting your team know when you need some time to yourself.
  • Develop your communication skills: Even though you may prefer written communication or one-on-one conversations, it’s important to be able to communicate effectively in various settings. Practice public speaking, participate in team discussions, and learn to articulate your ideas clearly and confidently.

It’s essential for introverts to advocate for their needs and communicate their preferred work styles to their colleagues and managers. One UX professional shared their experience on LinkedIn:

“I do my best work when I have time to think and prepare vs. on-demand thinking, speaking, & decision making. So, I ask for agendas, context, and pre-reads to help me make the most impact in meetings. When I shared this fact, it really helped my outgoing teammates, who never thought that others might operate differently than they do. I got feedback that this was a learning experience for them, and so I have continued to share this fact with new teammates to set expectations and advocate for myself since I find it to be an extrovert-centered business world.”

— Anonymous UXer on LinkedIn

Another LinkedIn UXer provided additional tactics for how they navigate communication styles and expectations, particularly in a fast-paced or extrovert-dominated environment.

“The longer I work with people in a creative capacity, the more I recognize the power of delay. Plenty of introverts are also high-achieving people pleasers (raises hand 🙋🏻). This has caused stress over the years when working with extroverts or verbal processors because there can be a perceived sense of urgency to every thought or ask.
[slowing things down] can look like using certain phrases to help slow down the implied urgency to allow me to more thoughtfully process the ask:

  • “Ah, interesting! Could you say more about that?”
  • “Can you clarify the ‘why’ behind this for me? I want to make sure I’ve got it right.”
  • “How does this support our goals for < x project / user >?”

And if the ask comes through asynchronously via email or Slack, I ask myself the following:

  1. Was this sent during working hours?
  2. Am I the only one who can answer this question / address this issue?
  3. Can I provide a short response that lets the person know their message was seen and that it’s on my radar?”

— Kait L. UXer via LinkedIn

Group Dynamics

Introverts may not initially thrive when it comes to group dynamics. They might wish to observe the group before deeply engaging. They can find it difficult to assert themselves in a group setting and may feel overwhelmed by the constant need for social interaction.

Additionally, introverts may find it harder to contribute to discussions or be slower to form meaningful connections with others in a group. The extroverted nature of group dynamics can be draining for introverts, and they may require more time to recharge after being in a group setting.

  • Prepare in advance: Gather your thoughts, jot down key points, or even practice your delivery. This can help you feel more confident and articulate in group settings.
  • Take breaks: If a meeting is dragging on, step out for a few minutes to recharge. A quick walk or a moment of solitude can do wonders for your energy levels.
  • Seek one-on-one interactions: If you’re struggling to be heard in a group, try scheduling separate meetings with key stakeholders to share your insights or design concepts in a more intimate setting.
  • Utilize virtual collaboration tools: If in-person meetings are particularly draining, suggest using tools like Slack, Miro, or Figma for asynchronous collaboration and feedback.

Introverts often find creative ways to navigate the challenges of large group settings. One UX researcher shared their experience on LinkedIn:

“I have a monthly meeting with many employees (50+) to go over survey results. I realized it was super awkward for me just to wait as people joined the meeting. I tried to make small talk about upcoming weekend plans or what people had done over the weekend, but engagement was still pretty low, and I was not equipped enough to carry on conversations. I decided to fill the time with memes. I would search for user research memes and tie them into why user research is important. More people started coming to my meetings just to see the meme! As time went on, I became known as the meme person. While I can’t necessarily say if that’s a good thing — brand awareness is powerful! At least people know user research exists and that we’re fun — even if it all started from being awkward and introverted.”

— Anonymous LinkedIn UXer

Guidance For Moving Up As An Introverted Researcher Or Designer

I turned to Tim Yeo to provide some insight into how introverts can best prepare for moving up the career ladder. Tim provided some tactical advice focusing on teamwork and people skills:

“Practice your people skills. If you, as an individual, could do it all on your own, you would’ve probably done it already. If you can’t, then you need to work with people to bring your creation to life. It takes a team.”

Tim also shared the strategic reason behind the importance of leaders having excellent people skills:

“We also like to believe that higher management is always more sure, more right, and has all the answers. In my experience, the reality is almost the opposite. Problems get fuzzier, messier, and more complex the higher up the organization you go. Making decisions with incomplete, imperfect information is the norm. To operate successfully in this environment requires steering people to your worldview, and that takes people skills.”

You can find some additional information on ways for introverts (and extroverts) to gain people skills in some of the .

Let’s move on and wrap up with some tips for those who are working alongside introverts.

Tips For Managers And Colleagues of Introverts

If you are a manager of a team consisting of more than yourself, you likely have an introvert among your team. Tim Yeo states, “Research from Susan Cain’s book, Quiet, shows that 1/3 to 1/2 of our population identify as quiet or introverted.”

Therefore,

“If you work in a diverse team, it follows that 1/3 to 1/2 of your team are quiet. So if you don’t create a space for quiet ones to be heard, that means you are missing out on 1/3 to 1/2 of ideas.”

UX managers of teams, including introverts and extroverts, should engage in some of the following suggested practices to create an inclusive work environment where everyone feels valued, heard, and able to contribute effectively to the team’s success. UX managers can use these tips to foster a diverse and productive team dynamic that drives innovation and creativity.

  • Flexibility
    • Offer communication options: Not everyone thrives in the same communication environment. Provide alternatives to large meetings, such as email updates, one-on-one check-ins, or asynchronous communication tools like Slack.
    • Embrace different work styles: Recognize that not everyone is most productive in a bustling office environment. Allow for flexible work arrangements, such as remote work or flexible hours, to accommodate different needs and preferences.
  • Value Diversity
    • Recognize the strengths of introverts: Introverts bring a unique perspective and valuable skills to the table. Encourage their contributions, celebrate their successes, and create an environment where they feel comfortable sharing their ideas.
    • Foster inclusivity: Make sure everyone on the team feels heard and valued, regardless of their personality type. Encourage open communication, active listening, and mutual respect.
  • Create Safe Spaces
    • Provide quiet spaces: Designate areas in the office where people can go to work independently or simply decompress.
    • Encourage breaks: Remind your team to take regular breaks throughout the day to recharge. This could involve stepping outside for fresh air, taking a short walk, or simply closing their eyes for a few minutes of meditation.
  • Professional Development
    • Offer tailored training: Provide opportunities for introverted researchers and designers to develop their communication and presentation skills in a supportive environment. This could involve workshops, coaching, or mentorship programs.

As a bonus, if you’re an introverted UX Manager and you are managing a team composed of introverts and extroverts, remember to encourage a variety of communication channels for your team members. You might default to your preferred style of communication but recognize that different team members may prefer different communication channels.

Some extroverted team members might enjoy brainstorming in large meetings, and introverted team members might prefer to contribute their ideas through written channels such as email, chat, or discussion boards.

Encouraging a variety of communication channels ensures that all team members feel comfortable sharing their thoughts and ideas.

Tim Yeo provided this list of tactics for encouraging and engaging introverts in participating in discussion:

  • Sharing the agenda before the meeting (so your quiet teammates, who are amazing preppers, by the way, can prepare and be ready to contribute).
  • Using a mix of silent and think-out-loud activities in meetings (so people who process information differently can all perform).
  • Give a heads-up before you call on a quiet colleague to speak.
  • Offer to be a thinking partner (when your quiet colleague appears to be stuck on a piece of work).

Now, let’s move on to focus on some tips for managing remote workers.

Recommendations For Managers And Teams Working Remotely

Managers and colleagues play a crucial role in creating a supportive and inclusive environment for introverted researchers and designers on dispersed teams. Here are some strategies to consider:

  1. Intentional Communication
    • Asynchronous First: Prioritize asynchronous communication methods (email, project management tools, shared documents) for brainstorming, feedback, and routine updates. This gives introverts time to process information and craft thoughtful responses.
    • One-on-One Check-Ins: Schedule regular one-on-one meetings with introverted team members to build rapport, discuss their concerns, and offer individualized support.
    • Mindful Meeting Management: Be mindful of meeting frequency and duration. Consider alternatives to video calls when possible, such as shared documents or asynchronous communication channels. When video calls are necessary, ensure they have a clear agenda and purpose.
  2. Creating Virtual Water Cooler Moments
    • Casual Communication Channels: Set up dedicated IM channels or virtual spaces for non-work-related conversations, allowing for informal social interaction and team bonding.
    • Virtual Social Events: Organize virtual coffee chats, game nights, or team-building activities to foster camaraderie and connection outside of work-related tasks.
    • Collaborative Tools: Utilize virtual whiteboards or shared documents for brainstorming sessions, encouraging participation and idea generation from all team members.
  3. Cultivating Empathy and Understanding
    • Education and Awareness: Share articles or resources about introversion with the team to foster understanding and appreciation for different personality types.
    • Open Dialogue: Encourage open conversations about communication styles and preferences, creating a safe space for everyone to express their needs.
    • Celebrate Strengths: Highlight the unique contributions that introverted team members bring to the table, such as their deep listening skills, thoughtful analysis, and ability to advocate for users.
  4. Leadership Support
    • Model Inclusive Behavior: Managers should lead by example, demonstrating respect for different communication styles and creating opportunities for all team members to contribute.
    • Provide Resources: Offer training or workshops on effective virtual communication and collaboration, tailoring them to the needs of introverted team members.
    • Check-In Regularly: Regularly touch base with introverted team members to gauge their well-being, address any concerns, and offer support.

Managers and teams can implement these strategies to create a work environment that values and empowers introverted researchers and designers, enabling them to thrive and make significant contributions to the team’s success.

Conclusion

We create a more inclusive and productive environment when we understand and appreciate the unique needs and preferences of introverts. Whether you’re an introverted UXer navigating the challenges of remote work or a manager looking to foster a diverse and engaged team, the strategies and insights shared in this article can help you unlock the full potential of introverted talent.

“The superpower of introspection that comes with introversion has enabled me to reflect on my behaviours and engineer myself to become more of an omnivert — able to adapt to different situations.

Being self-aware and working hard to ladder up through increasingly more challenging experiences has taken me from an introvert who was too concerned to tweet to an active leader in the community, delivering seminars, speaking at an international conference and now running a mentorship program for hundreds of UX professionals across the globe.”

— Chris C. UX Master Certified, via LinkedIn

Introversion is not a weakness to be overcome but a valuable asset to be embraced. We build stronger teams, foster innovation, and ultimately deliver more meaningful and impactful user experiences when we create a culture that celebrates both introverted and extroverted strengths. The best solutions often emerge from a blend of diverse perspectives, including the quiet voices that deserve to be heard.

In closing, I’d like to use the words of Tim Yeo, who provides us with some inspiration and positive reinforcement of who we are as introverts:

“You are enough. The world may continue to favour the extrovert ideal, but pretending to be someone you are not will never feel right. Know that there is a different path to have impact at work where you don’t have to pretend to be someone you are not. That path comes from tiny habits, done well, accumulated over time.”

[You can learn more about tiny habits in Tim’s book ]

Biography And Additional Resources

  • , Tim Yeo
  • “,” Pratik Joglekar
  • “,” Jeremy Bird
  • “,” Jonathan Walter
  • “,” Jesús R
  • , Susan Cain
  • “,” Angela Craven and SueAnne Hall
  • “,” Reinoud De Jong & Anne de la Croix
  • “” (PDF), Heather McCulloch
    A wonderful and fairly easy-to-read in-depth review of introversion and extroversion, including a discussion of the differences in nervous system functioning between introverts and extroverts. I strongly recommend reading this and the citations McCulloch provides if you are interested in an academic view on this topic.

SVG Coding Examples: Useful Recipes For Writing Vectors By Hand

Even though I am the kind of front-end engineer who manually cleans up SVG files when they are a mess, I never expected to become one of those people. You know, those crazy people that draw with code.

But here we are.

I dove deep into SVG specs last winter when I created a project to , and even though I knew the basic structures and rules of SVG, it was only then that I fully tried to figure out and understand what all of those numbers meant and how they interacted with each other.

And, once you get the hang of it, it is actually very interesting and quite fun to code SVG by hand.

No <path> ahead

We won’t go into more complex SVG shapes like in this article, this is more about practical information for simple SVGs. When it comes to drawing curves, I still recommend using a tool like Illustrator or Affinity. However, if you are super into compounding your lines, a path is useful. Maybe we’ll do that in Part 2.

Also, this guide focuses mostly on practical examples that illustrate some of the math involved when drawing SVGs. There is a wonderful article here that goes a bit deeper into the specs, which I recommend reading if you’re more interested in that: “.”

Drawing With Math. Remember Coordinate Systems?

Illustrator, Affinity, and all other vector programs are basically just helping you draw on a coordinate system, and then those paths and shapes are stored in SVG files.

If you open up these files in an editor, you’ll see that they are just a bunch of paths that contain lots of numbers, which are coordinates in that coordinate system that make up the lines.

But, there is a difference between the all-powerful <path> and the other, more semantic elements like <rect>, <circle>, <line>, <ellipse>, <polygon>, and <polyline>.

These elements are not that hard to read and write by hand, and they open up a lot of possibilities to add animation and other fun stuff. So, while most people might only think of SVGs as never-pixelated, infinitely scaling images, they can also be quite comprehensive pieces of code.

How Does SVG Work? unit != unit

Before we get started on how SVG elements are drawn, let’s talk about the ways units work in SVG because they might be a bit confusing when you first get started.

The beauty of SVG is that it’s a vector format, which means that the units are somewhat detached from the browser and are instead just relative to the coordinate system you’re working in.

That means you would not use a unit within SVG but rather just use numbers and then define the size of the document you’re working with.

So, your width and height might be using CSS rem units, but in your viewBox, units become just a concept that helps you in establishing sizing relationships.

What Is The viewBox?

The viewBox works a little bit like the CSS aspect-ratio property. It helps you establish a relationship between the width and the height of your coordinate system and sets up the box you’re working in. I tend to think of the viewBox as my “document” size.

Any element that is placed within the SVG with bigger dimensions than the viewBox will not be visible. So, the viewBox is the cutout of the coordinate system we’re looking through. The width and height attributes are unnecessary if there is a viewBox attribute.

So, in short, having an SVG with a viewBox makes it behave a lot like a regular image. And just like with images, it’s usually easiest to just set either a width or a height and let the other dimension be automatically sized based on the intrinsic aspect ratio dimensions.

So, if we were to create a function that draws an SVG, we might store three separate variables and fill them in like this:

`<svg 
  width="${svgWidth}" 
  viewBox="0 0 ${documentWidth} ${documentHeight}" 
  xmlns="http://www.w3.org/2000/svg"
>`;

SVG Things Of Note

There is a lot to know about SVG: When you want to reuse an image a lot, you may want to turn it into a symbol that can then be referenced with a use tag, you can create sprites, and there are some best practices when using them for icons, and so on.

Unfortunately, this is a bit out of the scope of this article. Here, we’re mainly focusing on designing SVG files and not on how we can optimize and use them.

However, one thing of note that is easier to implement from the start is accessibility.

SVGs can be used in an <img> tag, where alt tags are available, but then you lose the ability to interact with your SVG code, so inlining might be your preference.

When inlining, it’s easiest to declare role="img" and then add a <title> tag with your image title.

Note: You can check out .

<svg
  role="img"
  [...attr]
>
  <title>An accessible title</title>
  <!-- design code -->
</svg>

Drawing SVG With JavaScript

There is usually some mathematics involved when drawing SVGs. It’s usually fairly simple arithmetic (except, you know, in case you draw calligraphy grids and then have to dig out trigonometry…), but I think even for simple math, most people don’t write their SVGs in pure HTML and thus would like to use algebra.

At least for me, I find it much easier to understand SVG Code when giving meaning to numbers, so I always stick to JavaScript, and by giving my coordinates names, I like them immeasurable times more.

So, for the upcoming examples, we’ll look at the list of variables with the simple math and then JSX-style templates for interpolation, as that gives more legible syntax highlighting than string interpolations, and then each example will be available as a CodePen.

To keep this Guide framework-agnostic, I wanted to quickly go over drawing SVG elements with just good old vanilla JavaScript.

We’ll create a container element in HTML that we can put our SVG into and grab that element with JavaScript.

<div data-svg-container></div>
<script src="template.js"></script>

To make it simple, we’ll draw a rectangle <rect> that covers the entire viewBox and uses a fill.

Note: You can add all valid CSS values as fills, so a fixed color, or something like currentColor to access the site’s text color or a CSS variable would work here if you’re inlining your SVG and want it to interact with the page it’s placed in.

Let’s first start with our variable setup.

// vars
const container = document.querySelector("[data-svg-container]");
const svgWidth = "30rem"; // use any value with units here
const documentWidth = 100;
const documentHeight = 100;
const rectWidth = documentWidth;
const rectHeight = documentHeight;
const rectFill = "currentColor"; // use any color value here
const title = "A simple square box";

Method 1: Create Element and Set Attributes

This method is easier to keep type-safe (if using TypeScript) — uses proper SVG elements and attributes, and so on — but it is less performant and may take a long time if you have many elements.

const svg = document.createElementNS(", "svg");
const titleElement = document.createElementNS(", "title");
const rect = document.createElementNS(", "rect");

svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", 0 0 ${documentWidth} ${documentHeight});
svg.setAttribute("xmlns", ";
svg.setAttribute("role", "img");

titleElement.textContent = title;

rect.setAttribute("width", rectWidth);
rect.setAttribute("height", rectHeight);
rect.setAttribute("fill", rectFill);

svg.appendChild(titleElement);
svg.appendChild(rect);

container.appendChild(svg);

Here, you can see that with the same coordinates, a polyline won’t draw the line between the blue and the red dot, while a polygon will. However, when applying a fill, they take the exact same information as if the shape was closed, which is the right side of the graphic, where the polyline makes it look like a piece of a circle is missing.

This is the second time where we have dealt with quite a bit of repetition, and we can have a look at how we could leverage the power of JavaScript logic to render our template faster.

But first, we need a basic implementation like we’ve done before. We’re creating objects for the circles, and then we’re chaining the cx and cy values together to create the points attribute. We’re also storing our transforms in variables.

const polyDocWidth = 200;
const polyDocHeight = 200;
const circleOne = { cx: 25, cy: 80, r: 10, fill: "red" };
const circleTwo = { cx: 40, cy: 20, r: 5, fill: "lime" };
const circleThree = { cx: 70, cy: 60, r: 8, fill: "cyan" };
const points = ${circleOne.cx},${circleOne.cy} ${circleTwo.cx},${circleTwo.cy} ${circleThree.cx},${circleThree.cy};
const moveToTopRight = translate(${polyDocWidth / 2}, 0);
const moveToBottomRight = translate(${polyDocWidth / 2}, ${polyDocHeight / 2});
const moveToBottomLeft = translate(0, ${polyDocHeight / 2});

And then, we apply the variables to the template, using either a polyline or polygon element and a fill attribute that is either set to none or a color value.


<svg
  width={svgWidth}
  viewBox={`0 0 ${polyDocWidth} ${polyDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
>
  <title>Composite shape comparison</title>
  <g>
    <circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    />
    <circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    />
    <circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    />
    <polyline
      points={points}
      fill="none"
      stroke="black"
    />
  </g>
  <g transform={moveToTopRight}>
    <circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    />
    <circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    />
    <circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    />
    <polyline
      points={points}
      fill="white"
      stroke="black"
    />
  </g>
  <g transform={moveToBottomLeft}>
    <circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    />
    <circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    />
    <circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    />
    <polygon
      points={points}
      fill="none"
      stroke="black"
    />
  </g>
  <g transform={moveToBottomRight}>
    <circle
      cx={circleOne.cx}
      cy={circleOne.cy}
      r={circleOne.r}
      fill={circleOne.fill}
    />
    <circle
      cx={circleTwo.cx}
      cy={circleTwo.cy}
      r={circleTwo.r}
      fill={circleTwo.fill}
    />
    <circle
      cx={circleThree.cx}
      cy={circleThree.cy}
      r={circleThree.r}
      fill={circleThree.fill}
    />
    <polygon
      points={points}
      fill="white"
      stroke="black"
    />
  </g>
</svg>

And here’s a version of it to play with:

See the Pen by .

Dealing With Repetition

When it comes to drawing SVGs, you may find that you’ll be repeating a lot of the same code over and over again. This is where JavaScript can come in handy, so let’s look at the composite example again and see how we could optimize it so that there is less repetition.

Observations:

  • We have three circle elements, all following the same pattern.
  • We create one repetition to change the fill style for the element.
  • We repeat those two elements one more time, with either a polyline or a polygon.
  • We have four different transforms (technically, no transform is a transform in this case).

This tells us that we can create nested loops.

Let’s go back to just a vanilla implementation for this since the way loops are done is quite different across frameworks.

You could make this more generic and write separate generator functions for each type of element, but this is just to give you an idea of what you could do in terms of logic. There are certainly still ways to optimize this.

I’ve opted to have arrays for each type of variation that we have and wrote a helper function that goes through the data and builds out an array of objects with all the necessary information for each group. In such a short array, it would certainly be a viable option to just have the data stored in one element, where the values are repeated, but we’re taking the DRY thing seriously in this one.

The group array can then be looped over to build our SVG HTML.

const container = document.querySelector("[data-svg-container]");
const svgWidth = 200;
const documentWidth = 200;
const documentHeight = 200;
const halfWidth = documentWidth / 2;
const halfHeight = documentHeight / 2;
const circles = [
  { cx: 25, cy: 80, r: 10, fill: "red" },
  { cx: 40, cy: 20, r: 5, fill: "lime" },
  { cx: 70, cy: 60, r: 8, fill: "cyan" },
];
const points = circles.map(({ cx, cy }) => ${cx},${cy}).join(" ");
const elements = ["polyline", "polygon"];
const fillOptions = ["none", "white"];
const transforms = [
  undefined,
  translate(${halfWidth}, 0),
  translate(0, ${halfHeight}),
  translate(${halfWidth}, ${halfHeight}),
];
const makeGroupsDataObject = () => {
  let counter = 0;
  const g = [];
  elements.forEach((element) => {
    fillOptions.forEach((fill) => {
      const transform = transforms[counter++];
      g.push({ element, fill, transform });
    });
  });
  return g;
};
const groups = makeGroupsDataObject();
// result:
// [
//   {
//     element: "polyline",
//     fill: "none",
//   },
//   {
//     element: "polyline",
//     fill: "white",
//     transform: "translate(100, 0)",
//   },
//   {
//     element: "polygon",
//     fill: "none",
//     transform: "translate(0, 100)",
//   },
//   {
//     element: "polygon",
//     fill: "white",
//     transform: "translate(100, 100)",
//   }
// ]

const svg = document.createElementNS(", "svg");
svg.setAttribute("width", svgWidth);
svg.setAttribute("viewBox", 0 0 ${documentWidth} ${documentHeight});
svg.setAttribute("xmlns", ";
svg.setAttribute("role", "img");
svg.innerHTML = "<title>Composite shape comparison</title>";
groups.forEach((groupData) => {
  const circlesHTML = circles
    .map((circle) => {
      return &lt;circle 
          cx="${circle.cx}" 
          cy="${circle.cy}" 
          r="${circle.r}" 
          fill="${circle.fill}"
        /&gt;;
    })
    .join("");
  const polyElementHTML = &lt;${groupData.element} 
      points="${points}" 
      fill="${groupData.fill}" 
      stroke="black" 
    /&gt;;
  const group = &lt;g ${groupData.transform ?transform="${groupData.transform}": ""}&gt;
        ${circlesHTML}
        ${polyElementHTML}
      &lt;/g&gt;;
  svg.innerHTML += group;
});
container.appendChild(svg);

And here’s the Codepen of that:

See the Pen by .

More Fun Stuff

Now, that’s all the basics I wanted to cover, but there is so much more you can do with SVG. There is more you can do with transform; you can use a mask, you can use a marker, and so on.

We don’t have time to dive into all of them today, but since this started for me when making Calligraphy Grids, I wanted to show you the two most satisfying ones, which I, unfortunately, can’t use in the generator since I wanted to be able to open my generated SVGs in Affinity and it doesn’t support pattern.

Okay, so pattern is part of the defs section within the SVG, which is where you can define reusable elements that you can then reference in your SVG.

Graph Grid with pattern

If you think about it, a graph is just a bunch of horizontal and vertical lines that repeat across the x- and y-axis.

So, pattern can help us with that. We can create a <rect> and then reference a pattern in the fill attribute of the rect. The pattern then has its own width, height, and viewBox, which defines how the pattern is repeated.

So, let’s say we want to perfectly center our graph grid in any given width or height, and we want to be able to define the size of our resulting squares (cells).

Once again, let’s start with the JavaScipt variables:

const graphDocWidth = 226;
const graphDocHeight = 101;
const cellSize = 5;
const strokeWidth = 0.3;
const strokeColor = "currentColor";
const patternHeight = (cellSize / graphDocHeight) * 100;
const patternWidth = (cellSize / graphDocWidth) * 100;
const gridYStart = (graphDocHeight % cellSize) / 2;
const gridXStart = (graphDocWidth % cellSize) / 2;

Now, we can apply them to the SVG element:

<svg
  width={svgWidth}
  viewBox={`0 0 ${graphDocWidth} ${graphDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
>
  <defs>
    <pattern
      id="horizontal"
      viewBox={`0 0 ${graphDocWidth} ${strokeWidth}`}
      width="100%"
      height={`${patternHeight}%`}
    >
      <line
        x1="0"
        x2={graphDocWidth}
        y1={gridYStart}
        y2={gridYStart}
        stroke={strokeColor}
        stroke-width={strokeWidth}
      />
    </pattern>
    <pattern
      id="vertical"
      viewBox={`0 0 ${strokeWidth} ${graphDocHeight}`}
      width={`${patternWidth}%`}
      height="100%"
    >
      <line
        y1={0}
        y2={graphDocHeight}
        x1={gridXStart}
        x2={gridXStart}
        stroke={strokeColor}
        stroke-width={strokeWidth}
      />
    </pattern>
  </defs>
  <title>A graph grid</title>
  <rect
    width={graphDocWidth}
    height={graphDocHeight}
    fill="url(#horizontal)"
  />
  <rect
    width={graphDocWidth}
    height={graphDocHeight}
    fill="url(#vertical)"
  />
</svg>

And this is what that then looks like:

See the Pen by .

Dot Grid With pattern

If we wanted to draw a dot grid instead, we could simply repeat a circle. Or, we could alternatively use a line with a stroke-dasharray and stroke-dashoffset to create a dashed line. And we’d only need one line in this case.

Starting with our JavaScript variables:

const dotDocWidth = 219;
const dotDocHeight = 100;
const cellSize = 4;
const strokeColor = "black";
const gridYStart = (dotDocHeight % cellSize) / 2;
const gridXStart = (dotDocWidth % cellSize) / 2;
const dotSize = 0.5;
const patternHeight = (cellSize / dotDocHeight) * 100;

And then adding them to the SVG element:

<svg
  width={svgWidth}
  viewBox={`0 0 ${dotDocWidth} ${dotDocHeight}`}
  xmlns="http://www.w3.org/2000/svg"
  role="img"
>
  <defs>
    <pattern
      id="horizontal-dotted-line"
      viewBox={`0 0 ${dotDocWidth} ${dotSize}`}
      width="100%"
      height={`${patternHeight}%`}
    >
      <line
        x1={gridXStart}
        y1={gridYStart}
        x2={dotDocWidth}
        y2={gridYStart}
        stroke={strokeColor}
        stroke-width={dotSize}
        stroke-dasharray={`0,${cellSize}`}
        stroke-linecap="round"
      ></line>
    </pattern>
  </defs>
  <title>A Dot Grid</title>
  <rect
    x="0"
    y="0"
    width={dotDocWidth}
    height={dotDocHeight}
    fill="url(#horizontal-dotted-line)"
  ></rect>
</svg>

And this is what that looks like:

See the Pen by .

Conclusion

This brings us to the end of our little introductory journey into SVG. As you can see, coding SVG by hand is not as scary as it seems. If you break it down into the basic elements, it becomes quite like any other coding task:

  • We analyze the problem,
  • Break it down into smaller parts,
  • Examine each coordinate and its mathematical breakdown,
  • And then put it all together.

I hope that this article has given you a starting point into the wonderful world of coded images and that it gives you the motivation to delve deeper into the specs and try drawing some yourself.

Creating Custom Lottie Animations With SVGator

This article is a sponsored by

SVGator has gone through a series of updates since our last article, which was published in 2021, when it was already considered to be the most advanced web-based tool for vector animation. The first step toward more versatile software came with the mobile export feature that made it possible to implement the animations in iOS and Android applications.

The animation tool continued its upgrade with a series of new export options: video formats including MP4, AVI, MKV, MOV, and WebM, as well as image formats such as GIF, Animated PNG, WebP, and image sequence. By covering a larger area of users’ needs, the app now enables anyone to create animated stickers, social media, and newsletter animations, video assets, and many more types of visual content on demand.

The goal of becoming a “one tool for all” still lacked the last piece of the puzzle, namely full support for Lottie files. Lottie, just like SVG, is a vector-based format, but it has even better comprehensive multi-platform support, a fact that makes it super popular among developers and design professionals. It is built for use across various platforms, enabling smooth integration into both web and mobile applications. Its file size is minimal, it is infinitely scalable, and developers find it straightforward to implement once they get familiar with the format. Lottie can incorporate raster graphics and also supports interactivity.

SVGator’s latest version has everything you need for your various applications without the need for any third-party apps or plug-ins.

Note: You can test all of SVGator’s functionalities free of charge before committing to the Pro plan. However, you can export up to three watermarked files, with videos and GIFs limited to basic quality.

In this article, we will follow a creation process made of these steps:

  • Importing an existent Lottie JSON and making some minor adjustments;
  • Importing new animated assets created with SVGator (using the library);
  • Creating and animating new elements from scratch;
  • Exporting the Lottie animation.

Getting Started With SVGator

The sign-up process is simple, fast, and straightforward, and no credit card is required. Sign up either with Google or Facebook or, alternatively, by providing your name, email address, and password. Start a project either with a Lottie animation or a static SVG. If you don’t have an existing file, you can design and animate everything starting from a blank canvas.

Now that you’ve created your account, let’s dive right into the fun part. Here’s a preview of how your animation is going to look by the time you’re done following this guide. Neat, right?

()

Create A New Project

After logging in and clicking on the New Project option, you will be taken to the New Project Panel, where you can choose between starting from a blank project or uploading a file. Let’s start this project with an existing Lottie JSON.

  1. Click on the Upload file button and navigate to the directory where you have saved your Lottie file.

  2. Select the “Fast response.json” file and click Open.

    Hit play in the editor, and the animation should look like this:

    ()

Note: Make sure to hit Save after each step to make sure you don’t lose any of your progress while working on this project alongside our guide.

Import An Animated Asset

In this step, you will learn how to use the Library to import new assets to your project. You can easily choose from a variety of ready-made SVGs stored in different categories, load new files from your computer (Lottie, static SVG, and images), or save animations from other SVGator projects and reuse them.

In this case, let’s use an animated message bubble previously created and saved to the Uploads section of the Library.

Learn how to create and save animated assets with this .

  1. Navigate to the left sidebar of the app and switch to the Library tab, then click the “+” icon to upload the message bubble asset that you downloaded earlier.
  2. After it is loaded in the uploads section, simply click on it to add it to your project.

    All the animated properties of the asset are now present in the timeline, and you can edit them if you want.

    Note: Make sure the playhead is at the second “0” before adding the animated asset. When adding an animated asset, it will always start animating from the point where the playhead is placed.

  3. Freely adjust its position and size as you wish.
  4. With the playhead at the second 0, click on the Animate button, then choose Position.

At this point, you should have the first Position keyframe automatically added at the second 0, and you are ready to start animating.

Animate The Message Bubble

  1. Start by dragging the playhead on the timeline at 0.2 seconds:

  2. Then, drag the message bubble up a few pixels. The second keyframe will appear in the timeline, marking the element’s new position, thus creating the 2 milliseconds animation.

    Note: You can hit Play at any moment to check how everything looks!

    Next, you can use the Scale animator to make the bubble disappear after the dots representing the typing are done animating by scaling it down to 0 for both the X and Y axes:

  3. With the message bubble still selected, drag the playhead at 2.2 seconds, click on Animate, and select Scale (or just press Shift + S on the keyboard) to set the first Scale keyframe, then drag the playhead at 2.5 seconds.
  4. Set the scale properties to 0 for both the X and Y axes (in the right side panel). The bubble won’t be visible anymore at this point.

    Note: To maintain the ratio while changing the scale values, make sure you have the Maintain proportions on (the link icon next to the scale inputs).

    To add an extra touch of interest to this scaling motion, add an easing function preset:

  5. First, jump back to the first Scale keyframe (you can also double-click the keyframe to jump the playhead right at it).
  6. Open the Easing Panel next to the time indicator and scroll down through the presets list, then select Ease in Back. Due to its bezier going out of the graph, this easing function will create a bounce-back effect for the scale animation.

    Note: You can adjust the bezier of a selected easing preset and create a new custom function, which will appear at the top of the list.

    Keep in mind that you need at least one keyframe selected if you intend to apply an easing. The easing function will apply from the selected keyframe toward the next keyframe at its right. Of course, you can apply a certain easing for multiple keyframes at once.

    To get a smoother transition when the message bubble disappears, add an Opacity animation of one millisecond at the end of the scaling:

  7. Choose Opacity from the animators’ list and set the first keyframe at 2.4 seconds, then drag the playhead at 2.5 seconds to match the ending keyframe from the scale animation above.
  8. From the Appearance panel, drag the Opacity slider all the way to the left, at 0%.

Create An Email Icon

For the concept behind this animation to be complete, let’s create (and later animate) a “new email” notification as a response to the character sending that message.

Once again, SVGator’s asset library comes in handy for this step:

  1. Go to the search bar from the Library and type in “mail,” then click on the mail asset from the results.
  2. Place it somewhere above the laptop. Edit the mail icon to better fit the style of the animation:

  3. Open the email group and select the rectangle from the back.
  4. Change its fill color to a dark purple.
  5. Round up the corners using the Radius slider.

  6. Make the element’s design minimal by deleting these two lines from the lower part of the envelope.

  7. Select the envelope seal flap, which is the Polyline element in the group, above the rectangle.
  8. Add a lighter purple for the fill, set the stroke to 2 px width, and also make it white.

    To make the animation even more interesting, create a notification alert in the top-right corner of the envelope:

  9. Use the Ellipse tool (O) from the toolbar on top and draw a circle in the top-right corner of the envelope.
  10. Choose a nice red color for the fill, and set the stroke to white with a 2 px width.

  11. Click on the “T” icon to select the Text tool.
  12. Click on the circle and type “1”.
  13. Set the color to white and click on the “B” icon to make it bold.

  14. Select both the red circle and the number, and group them: right-click, and hit Group.

    You can also hit Command or Ctrl + G on your keyboard. Double-click on the newly created group to rename it to “Notification.”

  15. Select both the notification group and email group below and create a new group, which you can name “new email.”

Animate The New Email Group

Let’s animate the new email popping out of the laptop right after the character has finished texting his message:

  1. With the “New email” group selected, click twice on the Move down icon from the header to place the group last.
    You can also press Command or Ctrl + arrow down on your keyboard.
  2. Drag the group behind the laptop (on the canvas) to hide it entirely, and also scale it down a little.
  3. With the playhead at 3 seconds, add the animators Scale and Position.
    You can also do that by pressing Shift + S and Shift + P on your keyboard.

  4. Drag the playhead at the second 3.3 on the timeline.
  5. Move the New Email group above the laptop and scale it up a bit.
  6. You can also bend the motion path line to create a curved trajectory for the position animation.

  7. Select the first keyframes at the second 3.
  8. Open the easing panel.
  9. And click on the Ease Out Cubic preset to add it to both keyframes.

Animate The Notification

Let’s animate the notification dot separately. We’ll make it pop in while the email group shows up.

  1. Select the Notification group.
  2. Create a scale-up animation for it with 0 for both the X and Y axes at 3.2 and 1 at 3.5 seconds.
  3. Select the first keyframe and, from the easing panel, choose Ease Out Back. This easing function will ensure the popping effect.

Add Expressiveness To The Character

Make the character smile while looking at the email that just popped out. For this, you need to animate the stroke offset of the mouth:

  1. Select the mouth path. You can use the Node tool to select it directly with one click.
  2. Drag the playhead at 3.5 seconds, which is the moment from where the smile will start.
  3. Select the last keyframe of the Stroke offset animator from the timeline and duplicate it at second 3.5, or you can also use Ctrl or Cmd + D for duplication.

  4. Drag the playhead at second 3.9.
  5. Go to the properties panel and set the Offset to 0. The stroke will now fill the path all the way, creating a stroke offset animation of 4 milliseconds.

Final Edits

You can still make all kinds of adjustments to your animation before exporting it. In this case, let’s change the color of the initial Lottie animation we used to start this project:

  1. Use the Node tool to select all the green paths that form the character’s arms and torso.
  2. Change the color as you desire.

Export Lottie

Once you’re done editing, you can export the animation by clicking on the top right Export button and selecting the Lottie format. Alternatively, you can press Command or Ctrl + E on your keyboard to jump directly to the export panel, from where you can still select the animation you want to export.

  1. Make sure the Lottie format is selected from the dropdown. In the export panel, you can set a name for the file you are about to export, choose the frame rate and animation speed, or set a background color.
  2. You can preview the Lottie animation with a Lottie player.
    Note: This step is recommended to make sure all animations are supported in the Lottie format by previewing it on a webpage using the Lottie player. The preview in the export panel isn’t an actual Lottie animation.
  3. Get back to the export panel and simply click Export to download the Lottie JSON.

Final Thoughts

Now that you’re done with your animation don’t forget that you have plenty of export options available besides Lottie. You can post the same project on social media in video format, export it as an SVG animation for the web, or turn it into a GIF sticker or any other type of visual you can think of. GIF animations can also be used in Figma presentations and prototypes as a high-fidelity preview of the production-ready Lottie file.

We hope you enjoyed this article and that it will inspire you to create amazing Lottie animations in your next project.

Below, you can find a few useful resources to continue your journey with SVG and SVGator:

  • Check out a series of short video tutorials to help you get started with SVGator.
  • It answers the most common questions about SVGator, its features, and membership plans.

How To Build Custom Data Visualizations Using Luzmo Flex

This article is a sponsored by

In this article, I’ll introduce you to , a new feature from the Luzmo team who have been working hard making developer tooling to flatten the on-ramp for analytics reporting and data visualization.

With Luzmo Flex, you can hook up a dataset and create beautifully crafted, fully customizable interactive charts that meet your reporting needs. They easily integrate and interact with other components of your web app, allowing you to move away from a traditional “dashboard” interface and build more bespoke data products.

While many charting libraries offer similar features, I often found it challenging to get the data into the right shape that the library needed. In this article, I’ll show you how you can build beautiful data visualizations using the Google Analytics API, and you won’t have to spend any time “massaging” the data!

What Is Luzmo Flex?

Well, it’s two things, really. First of all, Luzmo is a low-code platform for embedded analytics. You can create datasets from just about anything, connect them to APIs like Google Analytics or your PostgreSQL database, or even upload static data in a .csv file and start creating data visualizations with drag and drop.

Secondly, Luzmo Flex is their new React component that can be configured to create custom data visualizations. Everything from the way you query your data to the way you display it can be achieved through code using the .

What makes Luzmo Flex unique is that you can reuse the core functionalities of Luzmo’s low-code embedded analytics platform in your custom-coded components.

That means, besides creating ready-to-use datasets, you can set up functions like the following out-of-the-box:

  • Multi-tenant analytics: Showing different data or visualizations to different users of your app.
  • Localization: Displaying charts in multiple languages, currencies, and timezones without much custom development.
  • Interactivity: Set up event listeners to create complex interactivity between Luzmo’s viz items and any non-Luzmo components in your app.

What Can You Build With Luzmo Flex?

By combining these off-the-shelf functions with flexibility through code, Luzmo Flex makes a great solution for building bespoke data products that go beyond the limits of a traditional dashboard interface. Below are a few examples of what that could look like.

Report Builder

A custom report builder that lets users search and filter a dataset and render it out using a number of different charts.

Filter Panel

Enable powerful filtering using HTML Select inputs, which will update each chart shown on the page.

Wearables Dashboard

Or how about a sleep tracker hooked up to your phone to track all those important snoozes?

When to Consider Luzmo Flex vs Chart Libraries

When building data-intensive applications, using something like , a well-known React charting library, you’ll likely need to reformat the data to fit the required shape. For instance, if I request the top 3 page views from the last seven days for my site, , I would have to use the Google Analytics API using the following query.

import dotenv from 'dotenv';
import { BetaAnalyticsDataClient } from '@google-analytics/data';
dotenv.config();

const credentials = JSON.parse(
  Buffer.from(process.env.GOOGLE_APPLICATION_CREDENTIALS_BASE64, 'base64').toString('utf-8')
);

const analyticsDataClient = new BetaAnalyticsDataClient({
  credentials,
});

const [{ rows }] = await analyticsDataClient.runReport({
  property: properties/${process.env.GA4&#95;PROPERTY&#95;ID},
  dateRanges: [
    {
      startDate: '7daysAgo',
      endDate: 'today',
    },
  ],
  dimensions: [
    {
      name: 'fullPageUrl',
    },
    {
      name: 'pageTitle',
    },
  ],
  metrics: [
    {
      name: 'totalUsers',
    },
  ],
  limit: 3,
  metricAggregations: ['MAXIMUM'],
});

The response would look something like this:

[
  {
    "dimensionValues": [
      {
        "value": ",
        "oneValue": "value"
      },
      {
        "value": "Paul Scanlon | Home",
        "oneValue": "value"
      }
    ],
    "metricValues": [
      {
        "value": "61",
        "oneValue": "value"
      }
    ]
  },
  {
    "dimensionValues": [
      {
        "value": ",
        "oneValue": "value"
      },
      {
        "value": "Paul Scanlon | A set of: "Sign In With Google" Buttons Made With Tailwind",
        "oneValue": "value"
      }
    ],
    "metricValues": [
      {
        "value": "41",
        "oneValue": "value"
      }
    ]
  },
  {
    "dimensionValues": [
      {
        "value": ",
        "oneValue": "value"
      },
      {
        "value": "Paul Scanlon | What Is a Proxy Redirect?",
        "oneValue": "value"
      }
    ],
    "metricValues": [
      {
        "value": "23",
        "oneValue": "value"
      }
    ]
  }
]

To make that data work with Recharts, I’d need to reformat it so it conforms to the following data shape.

[
  {
    "name": "Paul Scanlon | Home",
    "value": 61
  },
  {
    "name": "Paul Scanlon | A set of: "Sign In With Google" Buttons Made With Tailwind",
    "value": 41
  },
  {
    "name": "Paul Scanlon | What Is a Proxy Redirect?",
    "value": 23
  }
]

To accomplish this, I’d need to use an to iterate over each item, destructure the relevant data and return a key-value pair for the name and value for each.

const data = response.rows.map((row) => {
  const { dimensionValues, metricValues } = row;

  const pageTitle = dimensionValues[1].value;
  const totalUsers = parseInt(metricValues[0].value);

  return {
    name: pageTitle,
    value: totalUsers,
  };
});

And naturally, if you’re reformatting data this way in your application, you’d also want to write unit tests to ensure the data is always formatted correctly to avoid breaking your application… and all of this before you even get on to creating your charts!

With Luzmo Flex, all of this goes away, leaving you more time to focus on which data to display and how best to display it.

The First Steps to Building Bespoke Data Products

Typically, when building user interfaces that display data insights, your first job will be to figure out how to query the data source. This can take many forms, from RESTful API requests to direct database queries or sometimes reading from static files. Your next job will be figuring out when and how often these requests need to occur.

  • For data that rarely changes: Perhaps a query in the build step will work.
  • For data that changes regularly: A server-side request on page load.
  • For ever-changing data: A client-side request that polls an API on an interval.

Each will likely inform your application’s architecture, and there’s no single solution to this. Your last job, as mentioned, will be wrangling the responses, reformatting the data, and displaying it in the UI.

Below, I’ll show you how to do this using Luzmo Flex by using a simple example product.

What We’re Building: Custom Data Visualizations As Code

Here’s a screenshot of a simple data product I’ve built that displays three different charts for different reporting dimensions exposed by the Google Analytics API for page views for my site, , from the last seven days.

You can find all the code used in this article on the following link:

Getting Started With Luzmo

Before we get going, hop over to Luzmo and . You might also like to have a read of one of the getting started guides listed below. In this article, I’ll be using the Next.js starter.

Creating a Google Analytics Dataset

To create data visualization, you’ll first need data! To achieve this using Luzmo, head over to the dashboard, select Datasets from the navigation, and select GA4 Google Analytics. Follow the steps shown in the UI to connect Luzmo with your Google Analytics account.

With the setup complete, you can now select which reporting dimensions to add to your dataset. To follow along with this article, select Custom selection.

Lastly, select the following using the search input. Device Category, Page Title, Date, and Total users, then click Import when you’re ready.

You now have all the data required to build the Google Analytics dashboard. You can access the dataset ID from the URL address bar in your browser. You’ll need this in a later step.

If you’ve followed along from either of the first two getting started guides, you’ll have your API Key, API Token, App server, and API host environment variables set up and saved in a .env file.

Install Dependencies

If you’ve cloned one of the starter repositories, run the following to install the required dependencies.

npm install

Next, install the Luzmo React Embed dependency which exports the LuzmoVizItemComponent.

npm install  @luzmo/react-embed@latest

Now, find page.tsx located in the src/app directory, and add your dataset id as shown below.

Add the access object from the destructured response and pass access.datasets[0].id onto the LuzmoClientComponent component using a prop named datasetId.

// src/app/page.tsx


+ import dynamic from 'next/dynamic';

import Luzmo from '@luzmo/nodejs-sdk';
- import LuzmoClientComponent from './components/luzmo-client-component';
+ const LuzmoClientComponent = dynamic(() => import('./components/luzmo-client-component'), {
  ssr: false,
});


const client = new Luzmo({
  api_key: process.env.LUZMO_API_KEY!,
  api_token: process.env.LUZMO_API_TOKEN!,
  host: process.env.NEXT_PUBLIC_LUZMO_API_HOST!,
});

export default async function Home() {
  const response = await client.create('authorization', {
    type: 'embed',
    username: 'user id',
    name: 'first name last name',
    email: ',
    access: {
      datasets: [
        {
-          id: '<dataset_id>',
+          id: '42b43db3-24b2-45e7-98c5-3fcdef20b1a3',
          rights: 'use',
        },
      ],
    },
  });

-  const { id, token } = response;
+  const { id, token, access } = response;

-  return <LuzmoClientComponent authKey={id} authToken={token} />;
+  return <LuzmoClientComponent authKey={id} authToken={token} datasetId={access.datasets[0].id} />;
}

And lastly, find luzmo-client-component.tsx located in src/app/components. This is where you’ll be creating your charts.

Building a Donut Chart

The first chart you’ll create is a Donut chart that shows the various devices used by visitors to your site.

Add the following code to luzmo-client-component.tsx component.

// src/app/component/luzmo-client-component.tsx

'use client';

+ import { LuzmoVizItemComponent } from '@luzmo/react-embed';

interface Props {
  authKey: string;
  authToken: string;
+  datasetId: string;
}

- export default function LuzmoClientComponent({ authKey, authToken}: Props) {
+ export default function LuzmoClientComponent({ authKey, authToken, datasetId }: Props) {

+  const date = new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(); // creates a date 7 days ago

  console.log({ authKey, authToken });

  return (
    <section>
+    <div className='w-1/2 h-80'>
+      <LuzmoVizItemComponent
+        appServer={process.env.NEXT_PUBLIC_LUZMO_APP_SERVER}
+        apiHost={process.env.NEXT_PUBLIC_LUZMO_API_HOST}
+        authKey={authKey}
+        authToken={authToken}
+        type='donut-chart'
+        options={{
+          title: {
+            en: Devices from last 7 days,
+          },
+          display: {
+            title: true,
+          },
+          mode: 'donut',
+          legend: {
+            position: 'bottom',
+          },
+        }}
+        slots={[
+          {
+            name: 'measure',
+            content: [
+              {
+                label: {
+                  en: 'Total users',
+                },
+                column: '<column id>', // Total users
+                set: datasetId,
+                type: 'numeric',
+                format: '.4f',
+              },
+            ],
+          },
+          {
+            name: 'category',
+            content: [
+              {
+                label: {
+                  en: 'Device category',
+                },
+                column: '<column id>', // Device category
+                set: datasetId,
+                type: 'hierarchy',
+              },
+            ],
+          },
+        ]}
+        filters={[
+          {
+            condition: 'or',
+            filters: [
+              {
+                expression: '? >= ?',
+                parameters: [
+                  {
+                    column_id: '<column id>', // Date
+                    dataset_id: datasetId,
+                  },
+                  date,
+                ],
+              },
+            ],
+          },
+        ]}
+      />
+    <div/>
    </section>
  );
}

There’s quite a lot going on in the above code snippet, and I will explain it all in due course, but first, I’ll need to cover a particularly tricky part of the configuration.

Column IDs

You’ll notice the filters parameters, measure, and category content all require a column id.

In the filters parameters, the key is named column_id, and in the measure and category, the key is named column. Both of these are actually the column IDs from the dataset. And here’s how you can find them.

Back in the Luzmo dashboard, click into your dataset and look for the “more dots” next to each column heading. From the menu, select Copy column id. Add each column ID to the keys in the configuration objects.

In my example, I’m using the Total users for the measure, the Device category for the category, and the Date for the filter.

If you’ve added the column IDs correctly, you should be able to see a rendered chart on your screen!

… and as promised, here’s a breakdown of the configuration.

Initial Props Donut chart

The first part is fairly straightforward. appServer and authKey are the environment variables you saved to your .env file, and authKey and authToken are destructured from the authorization request and passed into this component via props.

The type prop determines which type of chart to render. In my example, I’m using donut-chart, but you could choose from one of the many options available, area-chart, bar-chart, bubble-chart, box-plot, and many more. You can see all the available options in the under .

<LuzmoVizItemComponent
  appServer={process.env.NEXT_PUBLIC_LUZMO_APP_SERVER}
  apiHost={process.env.NEXT_PUBLIC_LUZMO_API_HOST}
  authKey={authKey}
  authToken={authToken}
  type='donut-chart'

The one thing I should point out is my use of Tailwind classes: w-1/2 (width: 50%) and h-80 (height: 20rem). The LuzmoVizItemComponent ships with height 100%, so you’ll need to wrap the component with an element that has an actual height, or you won’t be able to see the chart on the page as it could be 100% of the height of an element with no height.

Donut Chart Options

The options object is where you can customize the appearance of your chart. It accepts many configuration options, among which:

  • A title for the chart that accepts a with corresponding text to display.
  • A display title value to determine if the title is shown or not.
  • A mode to determine if the chart is to be of type donut or pie chart.
  • A legend option to determine where the legend can be positioned.

All the available configuration options can be seen in the .

options={{
  title: {
    en: `Devices from last 7 days`,
  },
  display: {
    title: true,
  },
  mode: 'donut',
  legend: {
    position: 'bottom',
  },
}}

Donut Chart Slots

Slots are where you can configure which column from your dataset to use for the category and measure.

Slots can contain multiple measures, useful for displaying two columns of data per chart, but if more than two are used, one will become the measure.

Each measure contains a content array. The content array, among many other configurations, can include the following:

  • A label and locale,
  • The column id from the dataset,
  • The datasetId,
  • The type of data you’re displaying,
  • A format for the data.

The format used here is Python syntax for ; it’s similar to JavaScript’s .toFixed() method, e.g number.toFixed(4).

The hierarchy type is ​​the Luzmo standard data type. Any text column is considered as an data type.

You can read more in the about available configuration options for .

slots={[
  {
    name: 'measure',
    content: [
      {
        label: {
          en: 'Total users',
        },
        column: '<column id>', // Total users
        set: datasetId,
        type: 'numeric',
        format: '.4f',
      },
    ],
  },
  {
    name: 'category',
    content: [
      {
        label: {
          en: 'Device category',
        },
        column: '<column id>', // Device category
        set: datasetId,
        type: 'hierarchy',
      },
    ],
  },
]}

Donut Chart Filters

The filters object is where you can apply conditions that will determine which data will be shown. In my example, I only want to show data from the last seven days. To accomplish this, I first create the date variable:

const date = new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();

This would produce an ISO date string, e.g., 2024-08-21T14:25:40.088Z, which I can use with the filter. The filter uses Luzmo’s , to determine if the date for each row of the data is greater than or equal to the date variable. You can read more about Filter Expressions in Luzmo’s .

filters={[
  {
    condition: 'or',
    filters: [
      {
        expression: '? >= ?',
        parameters: [
          {
            column_id: '<column id>', // Date
            dataset_id: datasetId,
          },
          date,
        ],
      },
    ],
  },
]}

Building a Line Chart

The second chart you’ll be creating is a Line chart that displays the number of page views on each date from the last seven days from folks who visit your site.

Initial Props Line Chart

As with the Donut chart, the initial props are pretty much the same, but the type has been changed to line-chart.

<LuzmoVizItemComponent
  appServer={process.env.NEXT_PUBLIC_LUZMO_APP_SERVER}
  apiHost={process.env.NEXT_PUBLIC_LUZMO_API_HOST}
  authKey={authKey}
  authToken={authToken}
  type='line-chart'

Line Chart Options

The options for the Line chart are as follows, and the mode has been changed to line-chart.

options={{
  title: {
    en: `Site visits from last 7 days`,
  },
  display: {
    title: true,
  },
  mode: 'grouped',
}}

Line Chart Slots

The slots object is almost the same as before with the Donut chart, but for the Line chart, I’m using the date column from the dataset instead of the device category, and instead of category, I’m using the x-axis slot type. To ensure I’m formatting the data correctly (by day), I’ve used level 5. You can read .

slots={[
  {
    name: 'measure',
    content: [
      {
        label: {
          en: 'Total users',
        },
        column: '<column id>', // Total users
        set: datasetId,
        type: 'numeric',
        format: '.4f',
      },
    ],
  },
  {
    name: 'x-axis',
    content: [
      {
        label: {
          en: 'Date',
        },
        column: '<column id>', // Date
        set: datasetId,
        type: 'datetime',
        level: 5,
      },
    ],
  },
]}

Line Chart Filters

I’ve used the same filters as I used in the Donut chart.

Building a Bar Chart

The last chart you’ll be creating is a Bar chart that displays the number of page views for the top ten most viewed pages on your site.

Initial Props Bar Chart

As with the Donut and Line chart, the initial props are pretty much the same, but the type has been changed to bar-chart.

<LuzmoVizItemComponent
  className='w-full h-80'
  appServer={process.env.NEXT_PUBLIC_LUZMO_APP_SERVER}
  apiHost={process.env.NEXT_PUBLIC_LUZMO_API_HOST}
  authKey={authKey}
  authToken={authToken}
  type='bar-chart'

Bar Chart Options

The options for the Bar chart are a little more involved. I’ve included some styling options for the border-radii of the bars, limited the number of results to 10, and sorted the data by the highest page view count first using the sort by measure and direction options.

options={{
  title: {
    en: `Page views from last 7 days`,
  },
  display: {
    title: true,
  },
  mode: 'grouped',
  bars: {
    roundedCorners: 5,
  },
  limit: {
    number: 10,
  },
  sort: {
    by: 'measure',
    direction: 'desc',
  },
}}

Line Chart Slots

As with the Line chart, I’ve used an axis for one of the columns from the dataset. In this case, it’s the y-axis which displays the page title.

slots={[
  {
    name: 'measure',
    content: [
      {
        label: {
          en: 'Total users',
        },
        column: '<column id>', // Total users
        set: datasetId,
        type: 'numeric',
        format: '.4f',
      },
    ],
  },
  {
    name: 'y-axis',
    content: [
      {
        label: {
          en: 'Page title',
        },
        column: '<column id>', // Page title
        set: datasetId,
        type: 'hierarchy',
      },
    ],
  },
]}

Bar Chart Filters

I’ve used the same filters as I used in the Donut and Line chart.

What’s Next

As you can see, there are plenty of types of charts and customization options. Because this is just an “ordinary” React component, you can very easily make it configurable by an end user by allowing options to be set and unset using HTML input elements, checkbox, select, date, and so on.

But for me, the real power behind this is not having to mutate data!

This is particularly pertinent when displaying multiple charts with different reporting dimensions. Typically, this would require each to have their own utility function or reformatting method. That said, setting column IDs and dataset IDs is a little fiddly, but once you have the component hooked up to the dataset, you can configure and reconfigure as much as you like, all without having to rewrite data formatting functions.

If you’re interested in bringing data to life in your application and want to get it done without the usual headaches, to learn more!

Why Anticipatory Design Isn’t Working For Businesses

Consider the early days of the internet, when websites like NBC News and Amazon cluttered their pages with flashing banners and labyrinthine menus. In the early 2000s, Steve Krug’s book Don’t Make Me Think arrived like a lighthouse in a storm, advocating for simplicity and user-centric design.

Today’s digital world is flooded with choices, information, and data, which is both exciting and overwhelming. Unlike Krug’s time,

Today, the problem isn’t interaction complexity but opacity. AI-powered solutions often lack transparency and explainability, raising concerns about user trust and accountability.

The era of click-and-command is fading, giving way to a more seamless and intelligent relationship between humans and machines.

Expanding on Krug’s Call for Clarity: The Pillars of Anticipatory Design

Krug’s emphasis on clarity in design is more relevant than ever. In anticipatory design, clarity is not just about simplicity or ease of use — it’s about transparency and accountability. These two pillars are crucial but often missing as businesses navigate this new paradigm. Users today find themselves in a digital landscape that is not only confusing but increasingly intrusive. AI predicts their desires based on past behavior but rarely explains how these predictions are made, leading to growing mistrust.

Transparency is the foundation of clarity. It involves openly communicating how AI-driven decisions are made, what data is being collected, and how it is being used to anticipate needs. By demystifying these processes, designers can alleviate user concerns about privacy and control, thereby building trust.

Accountability complements transparency by ensuring that anticipatory systems are designed with ethical considerations in mind. This means creating mechanisms for users to understand, question, and override automated decisions if needed. When users feel that the system is accountable to them, their trust in the technology — and the brand — deepens.

What Makes a Service Anticipatory?

Image AI like a waiter at a restaurant. Without AI, they wait for you to interact with them and place your order. But with anticipatory design powered by AI and ML, the waiter can analyze your past orders (historical data) and current behavior (contextual data) — perhaps, by noticing you always start with a glass of sparkling water.

This proactive approach has evolved since the late 1990s, with early examples like Amazon’s recommendation engine and TiVo’s predictive recording. These pioneering services demonstrated the potential of predictive analytics and ML to create personalized, seamless user experiences.

Amazon’s Recommendation Engine (Late 1990s)

Amazon was a pioneer in using data to predict and suggest products to customers, setting the standard for personalized experiences in e-commerce.

TiVo (1999)

TiVo’s ability to learn users’ viewing habits and automatically record shows marked an early step toward predictive, personalized entertainment.

Netflix’s Recommendation System (2006)

Netflix began offering personalized movie recommendations based on user ratings and viewing history in 2006. It helped popularize the idea of anticipatory design in the digital entertainment space.

How Businesses Can Achieve Anticipatory Design

Designing for anticipation is designing for a future that is not here yet but has already started moving toward us.

Designing for anticipation involves more than reacting to current trends; it requires businesses to plan strategically for future user needs. Two critical concepts in this process are forecasting and backcasting.

  • Forecasting analyzes past trends and data to predict future outcomes, helping businesses anticipate user needs.
  • Backcasting starts with a desired future outcome and works backward to identify the steps needed to achieve that goal.

Think of it like planning a dream vacation. Forecasting would involve looking at your past trips to guess where you might go next. But backcasting lets you pick your ideal destination first, then plan the perfect itinerary to get you there.

Forecasting: A Core Concept for Future-Oriented Design

This method helps in planning and decision-making based on probable future scenarios. Consider Netflix, which uses forecasting to analyze viewers’ past viewing habits and predict what they might want to watch next. By leveraging data from millions of users, Netflix can anticipate individual preferences and serve personalized recommendations that keep users engaged and satisfied.

Backcasting: Planning From the Desired Future

Backcasting takes a different approach. Instead of using data to predict the future, it starts with defining a desired future outcome — a clear user intent. The process then works backward to identify the steps needed to achieve that goal. This goal-oriented approach crafts an experience that actively guides users toward their desired future state.

For instance, a financial planning app might start with a user’s long-term financial goal, such as saving for retirement, and then design an experience that guides the user through each step necessary to reach that goal, from budgeting tips to investment recommendations.

Integrating Forecasting and Backcasting In Anticipatory Design

The true power of anticipatory design emerges when businesses efficiently integrate both forecasting and backcasting into their design processes.

For example, Tesla’s approach to electric vehicles exemplifies this integration. By forecasting market trends and user preferences, Tesla can introduce features that appeal to users today. Simultaneously, by backcasting from a vision of a sustainable future, Tesla designs its vehicles and infrastructure to guide society toward a world where electric cars are the norm and carbon emissions are significantly reduced.

Over-Promising and Under-Delivering: The Pitfalls of Anticipatory Design

As businesses increasingly adopt anticipatory design, the integration of forecasting and backcasting becomes essential. Forecasting allows businesses to predict and respond to immediate user needs, while backcasting ensures these responses align with long-term goals. Despite its potential, anticipatory design often fails in execution, leaving few examples of success.

Over the past decade, I’ve observed and documented the rise and fall of several ambitious anticipatory design ventures. Among them, three — Digit, LifeBEAM Vi Sense Headphones, and Mint — highlight the challenges of this approach.

Digit: Struggling with Contextual Understanding

Digit aimed to simplify personal finance with algorithms that automatically saved money based on user spending. However, the service often missed the mark, lacking the contextual awareness necessary to accurately assess users’ real-time financial situations. This led to unexpected withdrawals, frustrating users, especially those living paycheck to paycheck. The result was a breakdown in trust, with the service feeling more intrusive than supportive.

LifeBEAM Vi Sense Headphones: Complexity and User Experience Challenges

LifeBEAM Vi Sense Headphones was marketed as an AI-driven fitness coach, promising personalized guidance during workouts. In practice, the AI struggled to deliver tailored coaching, offering generic and unresponsive advice. As a result, users found the experience difficult to navigate, ultimately limiting the product’s appeal and effectiveness. This disconnection between the promised personalized experience and the actual user experience left many disappointed.

Mint: Misalignment with User Goals

Mint aimed to empower users to manage their finances by providing automated budgeting tools and financial advice. While the service had the potential to anticipate user needs, users often found that the suggestions were not tailored to their unique financial situations, resulting in generic advice that did not align with their personal goals.

The lack of personalized, actionable steps led to a mismatch between user expectations and service delivery. This misalignment caused some users to disengage, feeling that Mint was not fully attuned to their unique financial journeys.

The Risks of Over-promising and Under-Delivering

The stories of Digit, LifeBEAM Vi Sense, and Mint underscore a common pitfall: over-promising and under-delivering. These services focused too much on predictive power and not enough on user experience. When anticipatory systems fail to consider individual nuances, they breed frustration rather than satisfaction, highlighting the importance of aligning design with human experience.

Digit’s approach to automated savings, for instance, became problematic when users found its decisions opaque and unpredictable. Similarly, LifeBEAM’s Vi Sense headphones struggled to meet diverse user needs, while Mint’s rigid tools failed to offer the personalized insights users expected. These examples illustrate the delicate balance anticipatory design must strike between proactive assistance and user control.

Failure to Evolve with User Needs

Many anticipatory services rely heavily on data-driven forecasting, but predictions can fall short without understanding the broader user context. Mint initially provided value with basic budgeting tools but failed to evolve with users’ growing needs for more sophisticated financial advice. Digit, too, struggled to adapt to different financial habits, leading to dissatisfaction and limited success.

Complexity and Usability Issues

Balancing the complexity of predictive systems with usability and transparency is a key challenge in anticipatory design.

When systems become overly complex, as seen with LifeBEAM Vi Sense headphones, users may find them difficult to navigate or control, compromising trust and engagement. Mint’s generic recommendations, born from a failure to align immediate user needs with long-term goals, further illustrate the risks of complexity without clarity.

Privacy and Trust Issues

Trust is critical in anticipatory design, particularly in services handling sensitive data like finance or health. Digit and Mint both encountered trust issues as users grew skeptical of how decisions were made and whether these services truly had their best interests in mind. Without clear communication and control, even the most sophisticated systems risk alienating users.

Inadequate Handling of Edge Cases and Unpredictable Scenarios

While forecasting and backcasting work well for common scenarios, they can struggle with edge cases or unpredictable user behaviors. If an anticipatory service can’t handle these effectively, it risks providing a poor user experience and, in the worst-case scenario, harming the user. Anticipatory systems must be prepared to handle edge cases and unpredictable scenarios.

LifeBEAM Vi Sense headphones struggled when users deviated from expected fitness routines, offering a one-size-fits-all experience that failed to adapt to individual needs. This highlights the importance of allowing users control, even when a system proactively assists them.

Designing for Anticipatory Experiences

Anticipatory design should empower users to achieve their goals, not just automate tasks.

We can follow a layered approach to plan a service that can evolve according to user actions and explicit ever-evolving intent.

But how do we design for intent without misaligning anticipation and user control or mismatching user expectations and service delivery?

At the core of this approach is intent — the primary purpose or goal that the design must achieve. Surrounding this are workflows, which represent the structured tasks to achieve the intent. Finally, algorithms analyze user data and optimize these workflows.

For instance, Thrive (see the image below), a digital wellness platform, aligns algorithms and workflows with the core intent of improving well-being. By anticipating user needs and offering personalized programs, Thrive helps users achieve sustained behavior change.

It perfectly exemplifies the three-layered concentric representation for achieving behavior change through anticipatory design:

1. Innermost layer: Intent

Improve overall well-being: Thrive’s core intent is to help users achieve a healthier and more fulfilling life. This encompasses aspects like managing stress, improving sleep quality, and boosting energy levels.

2. Middle layer: Workflows

Personalized programs and support: Thrive uses user data (sleep patterns, activity levels, mood) to create programs tailored to their specific needs and goals. These programs involve various workflows, such as:

  • Guided meditations and breathing exercises to manage stress and anxiety.
  • Personalized sleep routines aimed at improving sleep quality.
  • Educational content and coaching tips to promote healthy habits and lifestyle changes.

3. Outermost layer: Algorithms

Data analysis and personalized recommendations: Thrive utilizes algorithms to analyze user data and generate actionable insights. These algorithms perform tasks like the following:

  • Identify patterns in sleep, activity, and mood to understand user challenges.
  • Predict user behavior to recommend interventions that address potential issues.
  • Optimize program recommendations based on user progress and data analysis.

By aligning algorithms and workflows with the core intent of improving well-being, Thrive provides a personalized and proactive approach to behavior change. Here’s how it benefits users:

  • Sustained behavior change: Personalized programs and ongoing support empower users to develop healthy habits for the long term.
  • Data-driven insights: User data analysis helps users gain valuable insights into their well-being and identify areas for improvement.
  • Proactive support: Anticipates potential issues and recommends interventions before problems arise.

The Future of Anticipatory Design: Combining Anticipation with Foresight

Anticipatory design is inherently future-oriented, making it both appealing and challenging. To succeed, businesses must combine anticipation — predicting future needs — with foresight, a systematic approach to analyzing and preparing for future changes.

Foresight involves considering alternative future scenarios and making informed decisions to navigate toward desired outcomes. For example, Digit and Mint struggled because they didn’t adequately handle edge cases or unpredictable scenarios, a failure in their foresight strategy (see an image below).

As mentioned, while forecasting and backcasting work well for common scenarios, they can struggle with edge cases or unpredictable user behaviors. Under anticipatory design, if we demote foresight for a second plan, the business will fail to account for and prepare for emerging trends and disruptive changes. Strategic foresight helps companies to prepare for the future and develop strategies to address possible challenges and opportunities.

The Foresight process generally involves interrelated activities, including data research, trend analysis, planning scenarios, and impact assessment. The ultimate goal is to gain a broader and deeper understanding of the future to make more informed and strategic decisions in the design process and foresee possible frictions and pitfalls in the user experience.

Actionable Insights for Designer

  • Enhance contextual awareness
    Help data scientists or engineers to ensure that the anticipatory systems can understand and respond to the full context of user needs, not just historical data. Plan for pitfalls so you can design safety measures where the user can control the system.
  • Maintain user control
    Provide users with options to customize or override automated decisions, ensuring they feel in control of their experiences.
  • Align short-term predictions with long-term goals
    Use forecasting and backcasting to create a balanced approach that meets immediate needs while guiding users toward their long-term objectives.

Proposing an Anticipatory Design Framework

Predicting the future is no easy task. However, design can borrow foresight techniques to imagine, anticipate, and shape a future where technology seamlessly integrates with users evolving needs. To effectively implement anticipatory design, it’s essential to balance human control with AI automation. Here’s a 3-step approach to integrate future thinking into your workflow:

  1. Anticipate Directions of Change
    Identify major trends shaping the future.
  2. Imagine Alternative Scenarios
    Explore potential futures to guide impactful design decisions.
  3. Shape Our Choices
    Leverage these scenarios to align design with user needs and long-term goals.

This proposed framework (see an image above) aims to integrate forecasting and backcasting while emphasizing user intent, transparency, and continuous improvement, ensuring that businesses create experiences that are both predictive and deeply aligned with user needs.

Step 1: Anticipate Directions of Change

Objective: Identify the major trends and forces shaping the future landscape.

Components:

1. Understand the User’s Intent

  • User Research: Conduct in-depth user research through interviews, surveys, and observations to uncover user goals, motivations, pain points, and long-term aspirations or Jobs-to-be-Done (JTBD). This foundational step helps clearly define the user’s intent.
  • Persona Development: Develop detailed user personas that represent the target audience, including their long-term goals and desired outcomes. Prioritize understanding how the service can adapt in real-time to changing user needs, offering recommendations, or taking actions aligned with the persona’s current context.

2. Forecasting: Predicting Near-Term User Needs

  • Data Collection and Analysis: Collaborate closely with data scientists and data engineers to analyze historical data (past interactions), user behavior, and external factors. This collaboration ensures that predictive analytics enhance overall user experience, allowing designers to better understand the implications of data on user behaviors.
  • Predictive Modeling: Implement continuous learning algorithms that refine predictions over time. Regularly assess how these models evolve, adapting to users’ changing needs and circumstances.
  • Explore the Delphi Method: This is a structured communication technique that gathers expert opinions to reach a consensus on future developments. It’s particularly useful for exploring complex issues with uncertain outcomes. Use the Delphi Method to gather insights from industry experts, user researchers, and stakeholders about future user needs and the best strategies to meet those needs. The consensus achieved can help in clearly defining the long-term goals and desired outcomes.

Activities:

  • Conduct interviews and workshops with experts using the Delphi Method to validate key trends.
  • Analyze data and trends to forecast future directions.

Step 2: Imagine Alternative Scenarios

Objective: Explore a range of potential futures based on these changing directions.

Components:

1. Scenario Planning

  • Scenario Development: It involves creating detailed, plausible future scenarios based on various external factors, such as technological advancements, social trends, and economic changes. Develop multiple future scenarios that represent different possible user contexts and their impact on their needs.
  • Scenario Analysis: From these scenarios, you can outline the long-term goals that users might have in each scenario and design services that anticipate and address these needs. Assess how these scenarios impact user needs and experiences.

2. Backcasting: Designing from the Desired Future

  • Define Desired Outcomes: Clearly outline the long-term goals or future states that users aim to achieve. Use backcasting to reduce cognitive load by designing a service that anticipates future needs, streamlining user interactions, and minimizing decision-making efforts.
    • Use Visioning Planning: This is a creative process that involves imagining the ideal future state you want to achieve. It helps in setting clear, long-term goals by focusing on the desired outcomes rather than current constraints. Facilitate workshops or brainstorming sessions with stakeholders to co-create a vision of the future. Define what success looks like from the user’s perspective and use this vision to guide the backcasting process.
  • Identify Steps to Reach Goals: Reverse-engineer the user journey by starting from the desired future state and working backward. Identify the necessary steps and milestones and ensure these are communicated transparently to users, allowing them control over their experience.
  • Create Roadmaps: Develop detailed roadmaps that outline the sequence of actions needed to transition from the current state to the desired future state. These roadmaps should anticipate obstacles, respect privacy, and avoid manipulative behaviors, empowering users rather than overwhelming them.

Activities:

  • Develop and analyze alternative scenarios to explore various potential futures.
  • Use backcasting to create actionable roadmaps from these scenarios, ensuring they align with long-term goals.

Step 3: Shape Our Choices

Objective: Leverage these scenarios to spark new ideas and guide impactful design decisions.

Components:

1. Integrate into the Human-Centered Design Process

  • Iterative Design with Forecasting and Backcasting: Embed insights from forecasting and backcasting into every stage of the design process. Use these insights to inform user research, prototype development, and usability testing, ensuring that solutions address both predicted future needs and desired outcomes. Continuously refine designs based on user feedback.
  • Agile Methodologies: Adopt agile development practices to remain flexible and responsive. Ensure that the service continuously learns from user interactions and feedback, refining its predictions and improving its ability to anticipate needs.

2. Implement and Monitor: Ensuring Ongoing Relevance

  • User Feedback Loops: Establish continuous feedback mechanisms to refine predictive models and workflows. Use this feedback to adjust forecasts and backcasted plans as necessary, keeping the design aligned with evolving user expectations.
  • Automation Tools: Collaborate with data scientists and engineers to deploy automation tools that execute workflows and monitor progress toward goals. These tools should adapt based on new data, evolving alongside user behavior and emerging trends.
  • Performance Metrics: Define key performance indicators (KPIs) to measure the effectiveness, accuracy, and quality of the anticipatory experience. Regularly review these metrics to ensure that the system remains aligned with intended outcomes.
  • Continuous Improvement: Maintain a cycle of continuous improvement where the system learns from each interaction, refining its predictions and recommendations over time to stay relevant and useful.
    • Use Trend Analysis: This involves identifying and analyzing patterns in data over time to predict future developments. This method helps you understand the direction in which user behaviors, technologies, and market conditions are heading. Use trend analysis to identify emerging trends that could influence user needs in the future. This will inform the desired outcomes by highlighting what users might require or expect from a service as these trends evolve.

Activities:

  • Implement design solutions based on scenario insights and iterate based on user feedback.
  • Regularly review and adjust designs using performance metrics and continuous improvement practices.

Conclusion: Navigating the Future of Anticipatory Design

Anticipatory design holds immense potential to revolutionize user experiences by predicting and fulfilling needs before they are even articulated. However, as seen in the examples discussed, the gap between expectation and execution can lead to user dissatisfaction and erode trust.

To navigate the future of anticipatory design successfully, businesses must prioritize transparency, accountability, and user empowerment. By enhancing contextual awareness, maintaining user control, and aligning short-term predictions with long-term goals, companies can create experiences that are not only innovative but also deeply resonant with their users’ needs.

Moreover, combining anticipation with foresight allows businesses to prepare for a range of future scenarios, ensuring that their designs remain relevant and effective even as circumstances change. The proposed 3-step framework — anticipating directions of change, imagining alternative scenarios, and shaping our choices — provides a practical roadmap for integrating these principles into the design process.

As we move forward, the challenge will be to balance the power of AI with the human need for clarity, control, and trust. By doing so, businesses can fulfill the promise of anticipatory design, creating products and services that are not only efficient and personalized but also ethical and user-centric.

In the end,

The success of anticipatory design will depend on its ability to enhance, rather than replace, the human experience.

It is a tool to empower users, not to dictate their choices. When done right, anticipatory design can lead to a future where technology seamlessly integrates with our lives, making everyday experiences simpler, more intuitive, and ultimately more satisfying.

How To Create A Weekly Google Analytics Report That Posts To Slack

Google Analytics is great, but not everyone in your organization will be granted access. In many places I’ve worked, it was on a kind of “need to know” basis.

In this article, I’m gonna flip that on its head and show you how I wrote a that queries Google Analytics, generates a top ten list of the most frequently viewed pages on from the last seven days and compares them to the previous seven days to tell me which pages have increased in views, which pages have decreased in views, which pages have stayed the same and which pages are new to the list.

The report is then nicely formatted with icon indicators and posted to a public Slack channel every Friday at 10 AM.

Not only would this surfaced data be useful for folks who might need it, but it also provides an easy way to copy and paste or screenshot the report and add it to a slide for the weekly company/department meeting.

Here’s what the finished report looks like in Slack, and below, you’ll find a link to the GitHub Repository.

GitHub

To use this repository, follow the steps outlined in the README.

Prerequisites

To build this workflow, you’ll need admin access to your and Accounts and administrator privileges for GitHub Actions and Secrets for a GitHub repository.

Customizing the Report and Action

Naturally, all of the code can be changed to suit your requirements, and in the following sections, I’ll explain the areas you’ll likely want to take a look at.

Customizing the GitHub Action

The file name of the Action isn’t seen anywhere other than in the code/repo but naturally, change it to whatever you like, you won’t break anything.

The name and jobs: names detailed below are seen in the GitHub UI and Workflow logs.

The cron syntax determines when the Action will run. use and by changing the numbers you can determine when the Action runs.

You could also change the secrets variable names; just make sure you update them in your repository Settings.

# .github/workflows/weekly-analytics-report.yml

name: Weekly Analytics Report

on:
  schedule:
    - cron: '0 10 * * 5' # Runs every Friday at 10 AM UTC
  workflow_dispatch: # Allows manual triggering

jobs:
  analytics-report:
    runs-on: ubuntu-latest

    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      GA4_PROPERTY_ID: ${{ secrets.GA4_PROPERTY_ID }}
      GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'

      - name: Install dependencies
        run: npm install

      - name: Run the JavaScript script
        run: node src/services/weekly-analytics.js

Customizing the Google Analytics Report

The I’m using is set to pull the fullPageUrl and pageTitle for the totalUsers in the last seven days, and a for the previous seven days, and then aggregates the totals and limits the responses to 10.

You can use Google’s to construct your own query, then replace the requests.

// src/services/weekly-analytics.js#L75

const [thisWeek] = await analyticsDataClient.runReport({
  property: `properties/${process.env.GA4_PROPERTY_ID}`,
  dateRanges: [
    {
      startDate: '7daysAgo',
      endDate: 'today',
    },
  ],
  dimensions: [
    {
      name: 'fullPageUrl',
    },
    {
      name: 'pageTitle',
    },
  ],
  metrics: [
    {
      name: 'totalUsers',
    },
  ],
  limit: reportLimit,
  metricAggregations: ['MAXIMUM'],
});

Creating the Comparisons

There are two functions to determine which page views have increased, decreased, stayed the same, or are new.

The first is a simple reduce function that returns the URL and a count for each.

const lastWeekMap = lastWeekResults.reduce((items, item) => {
  const { url, count } = item;
  items[url] = count;
  return items;
}, {});

The second maps over the results from this week and compares them to last week.

// Generate the report for this week
const report = thisWeekResults.map((item, index) => {
  const { url, title, count } = item;
  const lastWeekCount = lastWeekMap[url];
  const status = determineStatus(count, lastWeekCount);

  return {
    position: (index + 1).toString().padStart(2, '0'), // Format the position with leading zero if it's less than 10
    url,
    title,
    count: { thisWeek: count, lastWeek: lastWeekCount || '0' }, // Ensure lastWeekCount is displayed as '0' if not found
    status,
  };
});

The final function is used to determine the status of each.

// Function to determine the status
const determineStatus = (count, lastWeekCount) => {
  const thisCount = Number(count);
  const previousCount = Number(lastWeekCount);

  if (lastWeekCount === undefined || lastWeekCount === '0') {
    return NEW;
  }

  if (thisCount > previousCount) {
    return HIGHER;
  }

  if (thisCount < previousCount) {
    return LOWER;
  }

  return SAME;
};

I’ve purposely left the code fairly verbose, so it’ll be easier for you to add console.log to each of the functions to see what they return.

Customizing the Slack Message

The I’m using creates a heading with an emoji, a divider, and a paragraph explaining what the message is.

Below that I’m using the context object to construct a and returning an object containing Slack specific message syntax which includes an icon, a count, the name of the page and a link to each item.

You can use Slack’s to construct your own message format.

// src/services/weekly-analytics.js#151 

    const slackList = report.map((item, index) => {
      const {
        position,
        url,
        title,
        count: { thisWeek, lastWeek },
        status,
      } = item;

      return {
        type: 'context',
        elements: [
          {
            type: 'image',
            image_url: ${reportConfig.url}/images/${status},
            alt_text: 'icon',
          },
          {
            type: 'mrkdwn',
            text: ${position}.  &lt;${url}|${title}&gt; | &#42;${x${thisWeek}}`* / x${lastWeek}`,
          },
        ],
      };
    });

Before you can run the GitHub Action, you will need to complete a number of Google, Slack, and GitHub steps.

Ready to get going?

Creating a Google Cloud Project

Head over to your , and from the dropdown menu at the top of the screen, click Select a project, and when the modal opens up, click NEW PROJECT.

Project name

On the next screen, give your project a name and click CREATE. In my example, I’ve named the project smashing-weekly-analytics.

Enable APIs & Services

In this step, you’ll enable the Google Analytics Data API for your new project. From the left-hand sidebar, navigate to APIs & Services > Enable APIs & services. At the top of the screen, click + ENABLE APIS & SERVICES.

Enable Google Analytics Data API

Search for “Google analytics data API,” select it from the list, then click ENABLE.

Create Credentials for Google Analytics Data API

With the API enabled in your project, you can now create the required credentials. Click the CREATE CREDENTIALS button at the top right of the screen to set up a new Service account.

A Service account allows an “application” to interact with Google APIs, providing the credentials include the required services. In this example, the credentials grant access to the Google Analytics Data API.

Service Account Credentials Type

On the next screen, select Google Analytics Data API from the dropdown menu and Application data, then click NEXT.

Service Account Details

On the next screen, give your Service account a name, ID, and description (optional). Then click CREATE AND CONTINUE.

In my example, I’ve given my service account a name and ID of smashing-weekly-analytics and added a short description that explains what the service account does.

Service Account Role

On the next screen, select Owner for the Role, then click CONTINUE.

Service Account Done

You can leave the fields blank in this last step and click DONE when you’re ready.

Service Account Keys

From the left-hand navigation, select Service Accounts, then click the “more dots” to open the menu and select Manage keys.

Service Accounts Add Key

On the next screen, locate the KEYS tab at the top of the screen, then click ADD KEY and select Create new key.

Service Accounts Download Keys

On the next screen, select JSON as the key type, then click CREATE to download your Google Application credentials .json file.

Google Application Credentials

If you open the .json file in your code editor, you should be looking at something similar to the one below.

In case you’re wondering, no, you can’t use an object as a variable defined in an .env file. To use these credentials, it’s necessary to convert the whole file into a base64 string.

Note: I wrote a more detailed post about how to use Google Application credentials as environment variables here: “.”

From your terminal, run the following: replace name-of-creds-file.json with the name of your .json file.

cat name-of-creds-file.json | base64

If you’ve already cloned the repo and followed the , add the base64 string returned after running the above and add it to the GOOGLE_APPLICATION_CREDENTIALS_BASE64 variable in your .env file, but make sure you wrap the string with double quotation makes.

GOOGLE_APPLICATION_CREDENTIALS_BASE64="abc123"

That completes the Google project side of things. The next step is to add your service account email to your Google Analytics property and find your Google Analytics Property ID.

Google Analytics Properties

Whilst your service account now has access to the Google Analytics Data API, it doesn’t yet have access to your Google Analytics account.

Get Google Analytics Property ID

To make queries to the Google Analytics API, you’ll need to know your Property ID. You can find it by heading over to your . Make sure you’re on the correct property (in the screenshot below, I’ve selected paulie.dev — GA4).

Click the admin cog in the bottom left-hand side of the screen, then click Property details.

On the next screen, you’ll see the PROPERTY ID in the top right corner. If you’ve already cloned the repo and followed the , add the property ID value to the GA4_PROPERTY_ID variable in your .env file.

Add Client Email to Google Analytics

From the Google application credential .json file you downloaded earlier, locate the client_email and copy the email address.

In my example, it looks like this: .

Now navigate to Property access management from the left hide side navigation and click the + in the top right-hand corner, then click Add users.

On the next screen, add the client_email to the Email addresses input, uncheck Notify new users by email, and select Viewer under Direct roles and data restrictions, then click Add.

That completes the Google Analytics properties section. Your “application” will use the Google application credentials containing the client_email and will now have access to your Google Analytics account via the Google Analytics Data API.

Slack Channels and Webhook

In the following steps, you’ll create a new Slack channel that will be used to post messages sent from your “application” using a .

Creating The Slack Channel

Create a new channel in your Slack workspace. I’ve named mine #weekly-analytics-report. You’ll need to set this up before proceeding to the next step.

Creating a Slack App

Head over to the dashboard, and click Create an App.

On the next screen, select From an app manifest.

On the next screen, select your Slack workspace, then click Next.

On this screen, you can give your app a name. In my example, I’ve named my Weekly Analytics Report. Click Next when you’re ready.

On step 3, you can just click Done.

With the App created, you can now set up a Webhook.

Creating a Slack Webhook

Navigate to Incoming Webhooks from the left-hand navigation, then switch the Toggle to On to activate incoming webhooks. Then, at the bottom of the screen, click Add New Webook to Workspace.

On the next screen, select your Slack workspace and a channel that you’d like to use to post messages, too, and click Allow.

You should now see your new Slack Webhook with a copy button. Copy the Webhook URL, and if you’ve already cloned the repo and followed the , add the Webhook URL to the SLACK_WEBHOOK_URL variable in your .env file.

Slack App Configuration

From the left-hand navigation, select Basic Information. On this screen, you can customize your app and add an icon and description. Be sure to click Save Changes when you’re done.

If you now head over to your Slack, you should see that your app has been added to your workspace.

That completes the Slack section of this article. It’s now time to add your environment variables to GitHub Secrets and run the workflow.

Add GitHub Secrets

Head over to the Settings tab of your GitHub repository, then from the left-hand navigation, select Secrets and variables, then click Actions.

Add the three variables from your .env file under Repository secrets.

A note on the base64 string: You won’t need to include the double quotes!

Run Workflow

To test if your Action is working correctly, head over to the Actions tab of your GitHub repository, select the Job name (Weekly Analytics Report), then click Run workflow.

If everything worked correctly, you should now be looking at a nicely formatted list of the top ten page views on your site in Slack.

Finished

And that’s it! A fully automated Google Analytics report that posts directly to your Slack. I’ve worked in a few places where Google Analytics data was on lockdown, and I think this approach to sharing Analytics data with Slack (something everyone has access to) could be super valuable for various people in your organization.

Sticky Headers And Full-Height Elements: A Tricky Combination

I was recently asked by a student to help with a seemingly simple problem. She’d been working on a website for a coffee shop that sports a sticky header, and she wanted the hero section right underneath that header to span the rest of the available vertical space in the viewport.

Here’s a visual demo of the desired effect for clarity.

Looks like it should be easy enough, right? I was sure (read: overconfident) that the problem would only take a couple of minutes to solve, only to find it was a much deeper well than I’d assumed.

Before we dive in, let’s take a quick look at the initial markup and CSS to see what we’re working with:

<body>
  <header class="header">Header Content</header>
  <section class="hero">Hero Content</section>
  <main class="main">Main Content</main>
</body>
.header {
  position: sticky;
  top: 0; /* Offset, otherwise it won't stick! */
}

/* etc. */

With those declarations, the .header will stick to the top of the page. And yet the .hero element below it remains intrinsically sized. This is what we want to change.

The Low-Hanging Fruit

The first impulse you might have, as I did, is to enclose the header and hero in some sort of parent container and give that container 100vh to make it span the viewport. After that, we could use Flexbox to distribute the children and make the hero grow to fill the remaining space.

<body>
  <div class="container">
    <header class="header">Header Content</header>
    <section class="hero">Hero Content</section>
  </div>
  <main class="main">Main Content</main>
</body>
.container {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.hero {
  flex-grow: 1;
}

/* etc. */

This looks correct at first glance, but watch what happens when scrolling past the hero.

See the Pen by .

The sticky header gets trapped in its parent container! But.. why?

If you’re anything like me, this behavior is unintuitive, at least initially. You may have heard that , meaning it participates in the normal flow of the document but only until it hits the edges of its scrolling container, at which point it becomes fixed. While viewing sticky as a combination of other values can be a useful mnemonic, it fails to capture one important difference between sticky and fixed elements:

A position: fixed element doesn’t care about the parent it’s nested in or any of its ancestors. It will break out of the normal flow of the document and place itself directly offset from the viewport, as though glued in place a certain distance from the edge of the screen.

Conversely, a position: sticky element will be pushed along with the edges of the viewport (or next closest scrolling container), but it will never escape the boundaries of its direct parent. Well, at least if you don’t count visually transform-ing it. So a better way to think about it might be, , that “position: sticky is, in a sense, a locally scoped position: fixed.” This is an intentional design decision, one that allows for section-specific sticky headers like the ones made famous by alphabetical lists in mobile interfaces.

See the Pen by .

Okay, so this approach is a no-go for our predicament. We need to find a solution that doesn’t involve a container around the header.

Fixed, But Not Solved

Maybe we can make our lives a bit simpler. Instead of a container, what if we gave the .header element a fixed height of, say, 150px? Then, all we have to do is define the .hero element’s height as height: calc(100vh - 150px).

See the Pen by .

This approach kinda works, but the downsides are more insidious than our last attempt because they may not be immediately apparent. You probably noticed that the header is too tall, and we’d wanna do some math to decide on a better height.

Thinking ahead a bit,

  • What if the .header’s children need to wrap or rearrange themselves at different screen sizes or grow to maintain legibility on mobile?
  • What if JavaScript is manipulating the contents?

All of these things could subtly change the .header’s ideal size, and chasing the right height values for each scenario has the potential to spiral into a maintenance nightmare of unmanageable breakpoints and magic numbers — especially if we consider this needs to be done not only for the .header but also the .hero element that depends on it.

I would argue that this workaround also just feels wrong. Fixed heights break one of the main affordances of CSS layout — the way elements automatically grow and shrink to adapt to their contents — and not relying on this usually makes our lives harder, not simpler.

So, we’re left with…

A Novel Approach

Now that we’ve figured out the constraints we’re working with, another way to phrase the problem is that we want the .header and .hero to collectively span 100vh without sizing the elements explicitly or wrapping them in a container. Ideally, we’d find something that already is 100vh and align them to that. This is where it dawned on me that display: grid may provide just what we need!

Let’s try this: We declare display: grid on the body element and add another element before the .header that we’ll call .above-the-fold-spacer. This new element gets a height of 100vh and spans the grid’s entire width. Next, we’ll tell our spacer that it should take up two grid rows and we’ll anchor it to the top of the page.

This element must be entirely empty because we don’t ever want it to be visible or to register to screen readers. We’re merely using it as a crutch to tell the grid how to behave.

<body>
  <!-- This spacer provides the height we want -->
  <div class="above-the-fold-spacer"></div>

  <!-- These two elements will place themselves on top of the spacer -->
  <header class="header">Header Content</header>
  <section class="hero">Hero Content</section>

  <!-- The rest of the page stays unaffected -->
  <main class="main">Main Content</main>
</body>
body {
  display: grid;
}

.above-the-fold-spacer {
  height: 100vh;
  /* Span from the first to the last grid column line */
  /* (Negative numbers count from the end of the grid) */
  grid-column: 1 / -1;
  /* Start at the first grid row line, and take up 2 rows */
  grid-row: 1 / span 2; 
}

/* etc. */

This is the magic ingredient.

By adding the spacer, we’ve created two grid rows that together take up exactly 100vh. Now, all that’s left to do, in essence, is to tell the .header and .hero elements to align themselves to those existing rows. We do have to tell them to start at the same grid column line as the .above-the-fold-spacer element so that they won’t try to sit next to it. But with that done… ta-da!

See the Pen by .

The reason this works is that a grid container can have multiple children occupying the same cell overlaid on top of each other. In a situation like that, the tallest child element defines the grid row’s overall height — or, in this case, the combined height of the two rows (100vh).

To control how exactly the two visible elements divvy up the available space between themselves, we can use the grid-template-rows property. I made it so that the first row uses min-content rather than 1fr. This is necessary so that the .header doesn’t take up the same amount of space as the .hero but instead only takes what it needs and lets the hero have the rest.

Here’s our full solution:


body {
  display: grid;
  grid-template-rows: min-content 1fr;
}

.above-the-fold-spacer {
  height: 100vh;
  grid-column: 1 / -1;
  grid-row: 1 / span 2;
}

.header {
  position: sticky;
  top: 0;
  grid-column-start: 1;
  grid-row-start: 1;
}

.hero {
  grid-column-start: 1;
  grid-row-start: 2;
}

And voila: A sticky header of arbitrary size above a hero that grows to fill the remaining visible space!

Caveats and Final Thoughts

It’s worth noting that the HTML order of the elements matters here. If we define .above-the-fold-spacer after our .hero section, it will overlay and block access to the elements underneath. We can work around this by declaring either order: -1, z-index: -1, or visibility: hidden.

Keep in mind that this is a simple example. If you were to add a sidebar to the left of your page, for example, you’d need to adjust at which column the elements start. Still, in the majority of cases, using a CSS Grid approach is likely to be less troublesome than the Sisyphean task of manually managing and coordinating the height values of multiple elements.

Another upside of this approach is that it’s adaptable. If you decide you want a group of three elements to take up the screen’s height rather than two, then you’d make the invisible spacer span three rows and assign the visible elements to the appropriate one. Even if the hero element’s content causes its height to exceed 100vh, the grid adapts without breaking anything. It’s even well-supported in all modern browsers.

The more I think about this technique, the more I’m persuaded that it’s actually quite clean. Then again, you know how lawyers can talk themselves into their own arguments? If you can think of an even simpler solution I’ve overlooked, feel free to reach out and let me know!