Designing Better UX For Left-Handed People Designing Better UX For Left-Handed People Vitaly Friedman 2025-07-25T15:00:00+00:00 2025-07-30T15:33:12+00:00 Many products — digital and physical — are focused on “average” users — a statistical representation of the user base, which often overlooks or dismisses anything that deviates from that average, […]
AccessibilityHandling JavaScript Event Listeners With Parameters Handling JavaScript Event Listeners With Parameters Amejimaobari Ollornwi 2025-07-21T10:00:00+00:00 2025-07-23T15:03:27+00:00 JavaScript event listeners are very important, as they exist in almost every web application that requires interactivity. As common as they are, it is also essential for them to […]
AccessibilityWhy Non-Native Content Designers Improve Global UX Why Non-Native Content Designers Improve Global UX Oleksii Tkachenko 2025-07-18T13:00:00+00:00 2025-07-23T15:03:27+00:00 A few years ago, I was in a design review at a fintech company, polishing the expense management flows. It was a routine session where we reviewed […]
AccessibilityWhat Does It Really Mean For A Site To Be Keyboard Navigable What Does It Really Mean For A Site To Be Keyboard Navigable Eleanor Hecks 2025-04-18T13:00:00+00:00 2025-06-25T15:04:30+00:00 Efficient navigation is vital for a functional website, but not everyone uses the internet the same way. […]
Accessibility
2025-04-18T13:00:00+00:00
2025-06-25T15:04:30+00:00
Efficient navigation is vital for a functional website, but not everyone uses the internet the same way. While most visitors either scroll on mobile or click through with a mouse, many people only use their keyboards. Up to 10 million American adults have carpal tunnel syndrome, which may cause pain when holding a mouse, and vision problems can make it difficult to follow a cursor. Consequently, you should keep your site keyboard navigable to achieve universal appeal and accessibility.
Keyboard navigation allows users to engage with your website solely through keyboard input. That includes using shortcuts and selecting elements with the Tab and Enter keys.
There are more than 500 keyboard shortcuts among operating systems and specific apps your audience may use. Standard ones for web navigation include Ctrl + F to find words or resources, Shift + Arrow to select text, and Ctrl + Tab to move between browser tabs. While these are largely the responsibilities of the software companies behind the specific browser or OS, you should still consider them.
Single-button navigation is another vital piece of keyboard navigability. Users may move between clickable items with the Tab and Shift keys, use the Arrow keys to scroll, press Enter or Space to “click” a link, and exit pop-ups with Esc.
The Washington Post homepage goes further. Pressing Tab highlights clickable elements as it should, but the first button press brings up a link to the site’s accessibility statement first. Users can navigate past this, but including it highlights how the design understands how keyboard navigability is a matter of accessibility.
You should understand how people may use these controls so you can build a site that facilitates them. These navigation options are generally standard, so any deviation or lack of functionality will stand out. Ensuring keyboard navigability, especially in terms of enabling these specific shortcuts and controls, will help you meet such expectations and avoid turning users away.
Keyboard navigability is crucial for a few reasons. Most notably, it makes your site more accessible. In the U.S. alone, over one in four people have a disability, and many such conditions affect technology use. For instance, motor impairments make it challenging for someone to use a standard mouse, and users with vision problems typically require keyboard and screen reader use.
Beyond accounting for various usage needs, enabling a wider range of control methods makes a site convenient. Using a keyboard rather than a mouse is faster when it works as it should and may feel more comfortable. Considering how workers spend nearly a third of their workweek looking for information, any obstacles to efficiency can be highly disruptive.
Falling short in these areas may lead to legal complications. Regulations like the Americans with Disabilities Act necessitate tech accessibility. While the ADA has no binding rules for what constitutes an accessible website, it specifically mentions keyboard navigation in its nonbinding guidance. Failing to support such functionality does not necessarily mean you’ll face legal penalties, but courts can use these standards to inform their decision on whether your site is reasonably accessible.
In 2023, Kitchenaid faced a class-action lawsuit for failing to meet such standards. Plaintiffs alleged that the company’s site didn’t support alt text or keyboard navigation, making it inaccessible to users with visual impairments. While the case ultimately settled out of court, it’s a reminder of the potential legal and financial repercussions of overlooking inclusivity.
Outside the law, an inaccessible site presents ethical concerns, as it shows preferential treatment for those who can use a mouse, even if that’s unintentional. Even without legal action, public recognition of this bias may lead to a drop in visitors and a tainted public image.
Thankfully, ensuring keyboard navigability is a straightforward user experience design practice. Because navigation is standard across OSes and browsers, keyboard-accessible sites employ a few consistent elements.
Web Accessibility In Mind states that sites must provide a visual indicator of elements currently in focus when users press Tab. Focus indicators are typically a simple box around the highlighted icon.
These are standard in CSS, but some designers hide them, so avoid using outline:0
or outline:none
to limit their visibility. You can also increase the contrast or change the indicator’s color in CSS.
The CNN Breaking News homepage is a good example of a strong focus indicator. Pressing Tab immediately brings up the box, which is bold enough to see easily and even uses a white border when necessary to stand out against black or dark-colored site elements.
The order in which the focus indicator moves between elements also matters. Generally speaking, pressing the Tab key should move it from left to right and top to bottom — the same way people read in English.
A few errors can stand in the way. Disabled buttons disrupt keyboard navigation flow by skipping an element with no explanation or highlighting it without making it clickable. Similarly, an interface where icons don’t fall in a predictable left-to-right, top-to-bottom order will make logical tab movement difficult.
The Sutton Maddock Vehicle Rental site is a good example of what not to do. When you press Tab, the focus indicator jumps from “Contact” to the Facebook link before going backward to the Twitter link. It starts at the right and moves left when it goes to the next line — the opposite order of what feels natural.
Skip links are also essential. These interactive elements let keyboard users jump to specific content without repeated keystrokes. Remember, these skips must be one of the first areas highlighted when you press Tab so they work as intended.
The HSBC Group homepage has a few skip navigation links. Pressing Tab pulls up three options, letting users quickly jump to whichever part of the site interests them.
Finally, all interactive elements on a keyboard-navigable site should be accessible via keystrokes. Anything people can click on or drag with a cursor should also support navigation and interaction. Enabling this is as simple as letting users select all items with the Tab or Arrow keys and press them with Space or Enter.
Appropriately, this Arizona State University page on keyboard accessibility showcases this concept well. All drop-down menus are possible to open by navigating to them via Tab and pressing Enter, so users don’t need a mouse to interact with them.
After designing a keyboard-accessible UX, you should test it to ensure that it works properly. The easiest way to do this is to explore the site solely with your keyboard. The chart below outlines the criteria to look for when determining whether your site is legitimately keyboard navigable.
Keyboard Navigable | Not Keyboard Navigable | |
---|---|---|
Clickable Elements | All elements are reachable through the keyboard and open when you press Enter. | Only some elements are possible to reach through the keyboard. Some links may be broken or not open when you press Enter. |
Focus Indicators | Pressing Tab, Space, or Enter brings up a focus indicator that is easy to see in all browsers. | Focus indicators may not appear when pressing all buttons. The box may be hard to see or only appear in some browsers. |
Skip Navigation Links | Pressing Tab for the first time pulls up at least one skip link to take users to much-visited content or menus. Continuing to press Tab moves the focus indicator past these links to highlight elements on the page as normal. | No skip links appear when pressing Tab for the first time. Alternatively, they appear after moving through all other elements. Skip links may not be functional. |
Screen Reader Support | Screen readers can read each element when highlighted with the focus indicator. | Some elements may not encourage any action from screen readers when highlighted. |
The Web Content Accessibility Guidelines outline two test rules to verify keyboard navigability:
Employ both standards to review your UX before making a site live.
Typical issues include the inability to highlight elements with the Tab key or things that don’t fall in a natural order. You can discover both problems by trying to access everything with your keyboard. However, you may prefer to conduct a navigability audit through a third party. Many private companies offer these services, but you can also use the Bureau of Internet Accessibility for a basic WCAG audit.
Keyboard navigability ensures you cater to all needs and preferences for an inclusive, accessible website design. While it’s straightforward to implement, it’s also easy to miss, so remember these principles when designing your UX and testing your site.
WCAG provides several techniques you can employ to meet keyboard accessibility standards and enhance your users’ experience:
Follow these guidelines and use WCAG’s test rules to create an accessible site. Remember to re-check it every time you add elements or change your UX.
Additionally, consider the following recommended reads to learn more about keyboards and their role in accessibility:
User-friendliness is an industry best practice that demonstrates your commitment to inclusivity for all. Even users without disabilities will appreciate intuitive, efficient keyboard navigation.
Fostering An Accessibility Culture Fostering An Accessibility Culture Daniel Devesa Derksen-Staats 2025-04-17T08:00:00+00:00 2025-06-25T15:04:30+00:00 A year ago, I learned that my role as an accessibility engineer was at risk of redundancy. It was a tough moment, both professionally and personally. For quite some time, my mind […]
Accessibility
2025-04-17T08:00:00+00:00
2025-06-25T15:04:30+00:00
A year ago, I learned that my role as an accessibility engineer was at risk of redundancy. It was a tough moment, both professionally and personally. For quite some time, my mind raced with guilt, self-doubt, plain sadness… But as I sat with these emotions, I found one line of thought that felt productive: reflection. What did I do well? What could I have done better? What did I learn?
Looking back, I realized that as part of a small team in a massive organization, we focused on a long-term goal that we also believed was the most effective and sustainable path: gradually shaping the organization’s culture to embrace accessibility.
Around the same time, I started listening to “Atomic Habits” by James Clear. The connection was immediate. Habits and culture are tightly linked concepts, and fostering an accessibility culture was really about embedding accessibility habits into everyone’s processes. That’s what we focused on. It took us time (and plenty of trial and error) to figure this out, and while there’s no definitive playbook for creating an accessibility program at a large organization, I thought it might help others if I shared my experiences.
Before we dive in, here’s a quick note: This is purely my personal perspective, and you’ll find a bias towards culture and action in big organizations. I’m not speaking on behalf of any employer, past or present. The progress we made was thanks to the incredible efforts of every member of the team and beyond. I hope these reflections resonate with those looking to foster an accessibility culture at their own companies.
To effectively shape habits, it’s crucial to focus on systems and processes (who we want to become) rather than obsessing over a final goal (or what we want to achieve). This perspective is especially relevant in accessibility.
Take the goal of making your app accessible. If you focus solely on achieving compliance without changing your systems (embedding accessibility into processes and culture), progress will be temporary.
For example, you might request an accessibility audit and fix the flagged issues to achieve compliance. While this can provide “quick” results, it’s often a short-lived solution.
Software evolves constantly: features are rewritten, old code is removed, and new functionality is added. Without an underlying system in place, accessibility issues can quickly resurface. Worse, this approach may reinforce the idea that accessibility is something external, checked by someone else, and fixed only when flagged. Not to mention that it becomes increasingly expensive the later accessibility issues are addressed in the process. It can also feel demoralizing when accessibility becomes synonymous with a long list of last-minute tickets when you are busiest.
Despite this, companies constantly focus on the goal rather than the systems.
“Accessibility is both a state and a practice.”
— Sommer Panage, SwiftTO talk, “Building Accessibility into Your Company, Team, and Culture”
I’ll take the liberty of tweaking that to an aspirational state. Without recognizing the importance of the practice, any progress made is at risk of regression.
Instead, I encourage organizations to focus on building habits and embedding good accessibility practices into their workflows. A strong system not only ensures lasting progress but also fosters a culture where accessibility becomes second nature.
That doesn’t mean goals are useless — they’re very effective in setting up direction.
In my team, we often said (only half-jokingly) that our ultimate goal was to put ourselves out of a job. This mindset reflects an important principle: accessibility is a cross-organizational responsibility, not the task of a single person or team.
That’s why, in my opinion, focusing solely on compliance rather than culture transformation (or prioritizing the “state” of accessibility over the “practice”) is a flawed strategy.
The real goal should be to build a user-centric culture where accessibility is embedded in every workflow, decision, and process. By doing so, companies can create products where accessibility is not about checking boxes and closing tickets but delivering meaningful and inclusive experiences to all users.
Different companies (of various sizes, structures, and cultures) will approach accessibility differently, depending on where they are in their journey. I still have to meet, though, an accessibility team that ever felt they had enough resources. This makes careful resource allocation a cornerstone of your strategy. And while there’s no one-size-fits-all solution, shifting left (addressing issues earlier in the development process) tends to be the most effective approach in most cases.
If your company has a design system, partnering with the team that owns it can be one of your biggest wins. Fixing a single component used across dozens of places improves the experience everywhere it’s used. This approach scales beautifully.
Involvement in foundational decisions and discussions, like choosing color palettes, typography, and component interactions, and so on, can also be very valuable. Contributing to documentation and guidelines tailored to accessibility can help teams across the organization make informed decisions.
For a deeper dive, I recommend Feli Bernutz’s excellent talk, “Designing APIs: How to Ensure Accessibility in Design Systems.”
It is worth repeating, you’ll need as many allies as possible. The more limited your resources, the more important this becomes. Something as simple as a Slack channel that becomes a safe space where people can ask questions and share tips can go a long way. Other ideas include lunch-and-learns, regular meetups, office hours, or building a more formal champions network. And, very importantly, it is about finding ways of recognising and celebrating wins and everyone’s good work.
If you’re exploring this, I highly recommend joining the Champions of Accessibility Network (CAN) group. It’s a great way to learn and connect with others who are passionate about accessibility.
Education is key for scaling accessibility efforts. While not everyone needs to be an expert, we should strive for everyone to know the basics. Repeatedly raising basic issues like missing accessibility labels, small target sizes, poor color contrast, and so on, can’t be productive.
Consider periodic training for different roles (PMs, designers, engineers…), embedding accessibility into onboarding sessions and documentation. You’ll need to find what works for you.
At Spotify, I found onboarding sessions for designers highly effective, as most features start with design. A Deque case study found that 67% of automatically detectable accessibility issues originate with design, reinforcing the importance of this approach. If your company has an education or training programme, partner with them. At Spotify, they were our biggest allies. They’ll help you get it right.
Everything that can be automated should eventually be automated. We know there’s already a lot on your plate, and automation should help lighten the load. This is especially true in larger organizations, where it can help scale efforts more efficiently. However, automated accessibility checks are not the silver bullet some might hope for.
One key issue is viewing automation as the solution rather than a safety net. Some companies claim automated tools catch as much as 57% of all issues or even 80% of issues by volume (PDF), though it is widely accepted that the figure is about 30%. Native mobile apps present greater challenges, making it likely that the real number is significantly lower for iOS and Android. These tools, and the high expectations around them, can create a false sense of security or reduce efforts to merely appease an automated tool of choice.
Automation doesn’t (and shouldn’t) replace intentionality. We should aim to deliver great accessible experiences from the start rather than wait for a tool to flag issues after the fact.
“
Whether your focus is on compliance or customer satisfaction, manual testing remains an essential part of the process. Whenever possible, you should also be testing with real users.
For me, the greatest value of automation is in catching basic regressions before release and serving as a gentle nudge to developers, reminding them to consider accessibility more thoughtfully. Ideally, they don’t just fix an issue and move on but take a moment to reflect:
When it comes to shaping habits, the environment matters. A strong accessibility culture isn’t built on willpower alone. It thrives on systems that encourage good practices and make bad ones harder to fall into. Nudges like automated checks, documentation, and proactive education are invaluable for keeping accessibility at the top of the mind.
I won’t lie; the moment I was first told my new job was to work on accessibility, I immediately jumped in, doing what I knew best, trying to fix as many issues as possible myself. While rewarding at first, this approach isn’t scalable in larger organizations. It can quickly lead to burnout. It also sets an expectation within the company that it’s your team’s responsibility to get it done, an expectation that becomes increasingly difficult to reset as time goes on.
Not saying you shouldn’t be hands-on, though! But you need to be strategic. Try to focus on supporting teams with complex issues, pair programming with colleagues, code reviews, or implementing cross-app improvements, ideally in partnership with the design system teams. This way, your efforts can have a broader impact.
Accessibility audits are another tool in your toolbox. Audits can be valuable but are often overused. They’re most effective after teams have done their best to make the product accessible, serving as a validation step rather than the starting point. After all, how useful is an audit if a significant portion of the flagged issues are basic problems that automated tools could have detected?
Alternatively, audits might help when you need quick results but don’t have the time or resources to upskill your workforce in time for a timely and necessary remediation.
While audits have their place and, as mentioned, can be valuable in certain situations, I wouldn’t rely on them to be the cornerstone of your strategy.
Try to find what works for your team, and, most importantly, adapt as circumstances change. Beyond the strategies mentioned, you might explore other initiatives:
It doesn’t mean one area of action is more important than another. Actually, in my view, one of the biggest reasons cultural change around accessibility takes longer than other areas is the lack of diversity in the workforce. Contributing to lines of action to address this issue might not be as immediately obvious as others.
The industry hasn’t done enough to hire people with disabilities, leaving them underrepresented in building products that truly work for them. Worse yet, they face more barriers in the hiring process. And even when they do get hired, they may find that the tools meant to enable us to do our work and be productive don’t work for them.
The key is to identify and lay out your areas of action first, then prioritize strategically while staying flexible as circumstances evolve. A thoughtful, adaptive approach ensures that no matter the challenge, your efforts remain impactful, avoiding stretching your team too thin and losing focus.
Here’s the truth that everyone working in accessibility inevitably and unfortunately faces sooner rather than later: accessibility done right, as we’ve seen so far, takes time. And that goes against the “move fast and break things” culture of quick results and short-termism that many companies still follow, even if they won’t openly admit it.
The slow-cooking nature of the process can, therefore, work against us. Being patient and trusting that small changes will aggregate and compound over time is incredibly challenging and sometimes nerve-racking. On top of that, if there’s a misalignment with leadership about what the ultimate goal is, or if there’s pressure to deliver quick results, it’s easy to feel like throwing in the towel, or worse, to experience burnout.
Unfortunately, burnout is an all-too-common issue in the accessibility community.
If you’d like to learn more about it, I highly recommend Shell Little’s talk, “The Accessibility to Burnout Pipeline.”
In those moments of doubt, it is useful to remember the quote embraced by the San Antonio Spurs NBA team, originally from social reformer Jacob Riis:
“When nothing seems to help, I go and look at a stonecutter hammering away at his rock perhaps a hundred times without as much as a crack showing in it. Yet at the hundred and first blow it will split in two, and I know it was not that blow that did it — but all that had gone before.”
— Jacob Riis
This serves as a powerful reminder that every small effort contributes to the eventual breakthrough, even when progress feels invisible.
Top-down approaches are easier, and yet, most accessibility initiatives start from the bottom. For a sustainable strategy, however, you’ll need both. If necessary, you’ll have to get buy-in from leadership or risk feeling like you’re constantly swimming upstream. Surprisingly, this is often harder than it seems. This topic could easily be an article on its own, but Vitaly Friedman offers some useful pointers in his piece “How To Make A Strong Case For Accessibility.”
In my experience, leadership buy-in is crucial to fostering an accessibility culture. Leaders often want to see how accessibility impacts the bottom line and whether investing in it is profitable. The hardest part is getting started, so if you can make a convincing case this way, do it.
I once watched a talk by Dave Dame titled “Stakeholders Agree That Accessibility Is Important, But That Does Not Mean They Will Invest In Accessibility.” He made an excellent point: You may need to speak the business language to get their attention. As Dave put it, “I have Cerebral Palsy, but my money doesn’t.”
There is also data out there suggesting that accessibility can be a worthwhile investment.
Still, I would encourage everyone to strive to change that mindset.
Doing accessibility for economic or legal reasons is valid, but it can lead to perverse incentives, where the bare minimum and compliance become the strategy, or where teams constantly need to prove their return on investment.
“
It is better to do it for the “wrong” reasons than not to do it at all. But ultimately, those aren’t the reasons we should be doing it.
The “13 Letters” podcast opened with an incredibly interesting two-part episode featuring Mike Shebanek. In it, Mike explains how Apple eventually renewed its commitment to accessibility because, in the state of Maine, schools were providing Macs and needed a screen reader for students who required one. It seems like a somewhat business-driven decision. But years later, Tim Cook famously stated, “When we work on making our devices accessible by the blind, I don’t consider the bloody ROI.” He also remarked, “Accessibility rights are human rights.”
That’s the mindset I wish more CEOs and leaders had. It is a story of how a change of mindset from “we have to do it” to “it is a core part of what we do” leads to a lasting and successful accessibility culture. Going beyond the bare minimum, Apple has become a leader in accessibility. An innovative company that consistently makes products more accessible and pushes the entire industry forward.
Once good habits are established, they tend to stick around. When I was let go, some people (I’m sure trying to comfort me) said the accessibility of the app would quickly regress and that the company would soon realize their mistake. Unexpectedly for them, I responded that I actually hoped it wouldn’t regress anytime soon. That, to me, would be the sign that I had done my job well.
And honestly, I felt confident it wouldn’t. Incredible people with deep knowledge and a passion for accessibility and building high-quality products stayed at the company. I knew the app was in good hands.
But it’s important not to fall into complacency. Cultures can be taken for granted, but they need constant nurturing and protection. A company that hires too fast, undergoes a major layoff, gets acquired, experiences high turnover, or sees changes in leadership or priorities… Any of these can pretty quickly destabilize something that took years to build.
This might not be your experience, and what we did may not work for you, but I hope you find this insight useful. I have, as they say, strong opinions, but loosely held. So I’m looking forward to knowing what you think and learning about your experiences too.
There’s no easy way or silver bullet! It’s actually very hard! The odds are against you. And we tend to constantly be puzzled about why the world is against us doing something that seems so obviously the right thing to do: to invite and include as many people as possible to use your product, to remove barriers, to avoid exclusion. It is important to talk about exclusion, too, when we talk about accessibility.
“Even though we were all talking about inclusion, we each had a different understanding of that word. Exclusion, on the other hand, is unanimously understood as being left out (…) Once we learn how to recognize exclusion, we can begin to see where a product or experience that works well for some might have barriers for someone else. Recognizing exclusion sparks a new kind of creativity on how a solution can be better.”
Something that might help: always assume goodwill and try to meet people where they are. I need to remind myself of this quite often.
“It is all about understanding where people are, meeting them where they’re at (…) People want to fundamentally do the right thing (…) They might not know what they don’t know (…) It might mean stepping back and going to the fundamentals (…) I know some people get frustrated about having to re-explain accessibility over and over again, but I believe that if we are not willing to do that, then how are we gonna change the hearts and minds of people?”
I’d encourage you to:
But honestly, anything you can do is progress. And progress is all we need, just for things to be a little better every day. Your job is incredibly important. Thanks for all you do!
Accessibility: This is the way!
Inclusive Dark Mode: Designing Accessible Dark Themes For All Users Inclusive Dark Mode: Designing Accessible Dark Themes For All Users Alex Williams 2025-04-15T13:00:00+00:00 2025-06-25T15:04:30+00:00 Dark mode, a beloved feature in modern digital interfaces, offers a visually striking alternative to traditional light themes. Its allure lies […]
Accessibility
2025-04-15T13:00:00+00:00
2025-06-25T15:04:30+00:00
Dark mode, a beloved feature in modern digital interfaces, offers a visually striking alternative to traditional light themes. Its allure lies in the striking visual contrast it provides, a departure from the light themes that have dominated our screens for decades.
However, its design often misses the mark on an important element — accessibility. For users with visual impairments or sensitivities, dark mode can introduce significant challenges if not thoughtfully implemented.
Hence, designing themes with these users in mind can improve user comfort in low-light settings while creating a more equitable digital experience for everyone. Let’s take a look at exactly how this can be done.
Dark mode can offer tangible accessibility benefits when implemented with care. For many users, especially those who experience light sensitivity, a well-calibrated dark theme can reduce eye strain and provide a more comfortable reading experience. In low-light settings, the softer background tones and reduced glare may help lessen fatigue and improve visual focus.
However, these benefits are not universal. For some users, particularly those with conditions such as astigmatism or low contrast sensitivity, dark mode can actually compromise readability. Light text on a dark background may lead to blurred edges or halo effects around characters, making it harder to distinguish content.
When you’re designing, contrast isn’t just another design element, it’s a key player in dark mode’s overall readability and accessibility. A well-designed dark mode, with the right contrast, can also enhance user engagement, creating a more immersive experience and drawing users into the content.
First and foremost, cleverly executing your site’s dark mode will result in a lower bounce rate (as much as 70%, according to one case study from Brazil). You can then further hack this statistic and greet visitors with a deep black, reinforcing your rankings in organic search results by sending positive signals to Google.
How is this possible? Well, the darker tones can hold attention longer, especially in low-light settings, leading to higher interaction rates while making your design more accessible. The point is, without proper contrast, even the sleekest dark mode design can become difficult to navigate and uncomfortable to use.
Instead of using pure black backgrounds, which can cause eye strain and make text harder to read, opt for dark grays. These softer tones help reduce harsh contrast and provide a modern look.
However, it’s important to note that color adjustments alone don’t solve technical challenges like anti-aliasing. In dark mode, anti-aliasing has the problem of halo effects, where the edges of the text appear blurred or overly luminous. To mitigate these issues, designers should test their interfaces on various devices and browsers and consider CSS properties to improve text clarity.
Real-world user testing, especially with individuals who have visual impairments, is essential to fine-tune these details and ensure an accessible experience for all users.
For individuals with low vision or color blindness, the right contrast can mean the difference between a frustrating and a seamless user experience. To keep your dark mode design looking its best, don’t forget to also:
These simple adjustments make a big difference in creating a dark mode that everyone can use comfortably.
While dark themes provide a sleek and visually appealing interface, some features still require lighter colors to remain functional and readable.
Certain interactive elements like buttons or form fields need to be easily distinguishable, especially if it involves transactions or providing personal information. Simply put, no one wants to sign documents digitally if they have to look for the right field, nor do they want to make a transaction if there is friction.
In addition to human readability, machine readability is equally important in an age of increased automation. Machine readability refers to how effective computers and bots are at extracting and processing data from the interface without human intervention. It’s important for pretty much any type of interface that has automation built into the workflows. For example, if the interface utilizes machine learning, machine readability is essential. Machine learning relies on accurate, quality data and effective interaction between different modules and systems, which makes machine readability critical to make it effective.
You can help ensure your dark mode interface is machine-readable in the following ways:
<header>
, <nav>
, <main>
, and <footer>
) and ARIA roles. When your code is organized this way, machines can read and understand your page better, regardless of whether it’s in dark or light mode.Making sure that data, especially in automated systems, is clear and accessible prevents functionality breakdowns and guarantees seamless workflows.
Although we associate visual accessibility with visual impairments, the truth is that it’s actually meant for everyone. Easier access is something we all strive for, right? But more than anything, practicality is what matters. Fortunately, the strategies below fit the description to a tee.
Contrast is the backbone of dark mode design. Without proper implementation, elements blend together, creating a frustrating user experience. Instead of looking at contrast as just a relationship between colors, try to view it in the context of other UI elements:
The use of effective typography is vital for preserving readability in dark mode. In particular, the right font choice can make your design both visually appealing and functional, while the wrong one can cause strain and confusion for users.
Thus, when designing dark themes, it’s essential to prioritize text clarity without sacrificing aesthetics. You can do this by prioritizing:
Colors in dark mode require a delicate balance to ensure accessibility. It’s not as simple as looking at a list of complimentary color pairs and basing your designs around them. Instead, you must think about how users with visual impairments will experience the dark theme design.
While avoiding color combinations like red and green for the sake of colorblind users is a widely known rule, visual impairment is more than just color blindness. In particular, you have to pay attention to:
As you can see, there are a lot of different considerations. Something you need to account for is that it’s nigh-on impossible to have a solution that will fix all the issues. You can’t test an interface for every single individual who uses it. The best you can do is make it as accessible as possible for as many users as possible, and you can always make adjustments in later iterations if there are major issues for a segment of users.
Even though dark mode doesn’t target only users with visual impairments, their input and ease of use are perhaps the most important.
The role of color perception in dark mode varies significantly among users, especially for those with visual impairments like color blindness or low vision. These conditions can make it challenging to distinguish certain colors on dark backgrounds, which can affect how users navigate and interact with your design.
In particular, some colors that seem vibrant in light mode may appear muted or blend into the background, making it difficult for users to see or interact with key elements. This is exactly why testing your color palette across different displays and lighting conditions is essential to ensure consistency and accessibility. However, you probably won’t be able to test for every single screen type, device, or environmental condition. Once again, make the dark mode interface as accessible as possible, and make adjustments in later iterations based on feedback.
For users with visual impairments, accessible color palettes can make a significant difference in their experience. Interactive elements, such as buttons or links, need to stand out clearly from the rest of the design, using colors that provide strong contrast and clear visual cues.
In the example above, Slack did an amazing job providing users with visual impairments with premade options. That way, someone can save hours of valuable time. If it wasn’t obvious by now, apps that do this find much more success in customer attraction (and retention) than those that don’t.
Dark mode is often celebrated for its ability to reduce screen glare and blue light, making it more comfortable for users who experience certain visual sensitivities, like eye strain or discomfort from bright screens.
For many, this creates a more pleasant browsing experience, particularly in low-light environments. However, dark mode isn’t a perfect solution for everyone.
Users with astigmatism, for instance, may find it difficult to read light text on a dark background. The contrast can cause the text to blur or create halos, making it harder to focus. Likewise, some users prefer dark mode for its reduced eye strain, while others may find it harder to read or simply prefer light mode.
These different factors mean that adaptability is important to better accommodate users who may have certain visual sensitivities. You can allow users to toggle between dark and light modes based on their preferences. For even greater comfort, think of providing options to customize text colors and background shades.
Switching between dark and light modes should also be smooth and unobtrusive. Whether you’re working in a bright office or relaxing in a dimly lit room, the transition should never disrupt your workflow.
On top of that, remembering your preferences automatically for future sessions creates a consistent and thoughtful user experience. These adjustments turn dark mode into a truly personalized feature, tailored to elevate every interaction you have with the interface.
While dark mode offers benefits like reduced eye strain and energy savings, it still has its limits. Focusing on key elements like contrast, readability, typography, and color perception helps guarantee that your designs are inclusive and user-friendly for all of your users.
Offering dark mode as an optional, customizable feature empowers users to interact with your interface in a way that best suits their needs. Meanwhile, prioritizing accessibility in dark mode design creates a more equitable digital experience for everyone, regardless of their abilities or preferences.
How To Build A Business Case To Promote Accessibility In Your B2B Products How To Build A Business Case To Promote Accessibility In Your B2B Products Gloria Diaz Alonso 2025-04-04T12:00:00+00:00 2025-06-25T15:04:30+00:00 When I started working on promoting accessibility, I was fully convinced of its value […]
Accessibility
2025-04-04T12:00:00+00:00
2025-06-25T15:04:30+00:00
When I started working on promoting accessibility, I was fully convinced of its value and was determined to bring it to the business stakeholders. I thought that the moment I started pushing for it inside the company, my key stakeholders would be convinced, committed, and enlightened, and everyone would start working to make it possible.
I prepared a lovely presentation about the benefits of accessibility. I made sure my presentation reflected that accessibility is the right thing to do: it is good for everyone, including those who don’t have a disability; it improves usability, makes the code more robust, and, of course, promotes inclusivity. I confidently shared it with my stakeholders. I was so excited. Aaaaaand BOOM… I hit a wall. They didn’t show much interest. I repetitively got comments, such as:
“People don’t manage to understand the real value. How can they say it has no impact?” I thought. After some time of processing my frustration and thinking about it, I realized that maybe I was not communicating the value correctly. I was not speaking the same language, and I was just approaching it from my perspective. It was just a presentation, not a business case.
If there is something I had to learn when working that I didn’t in university, it is that if you want to move things forward in a company, you have to have a business case. I never thought that being a UX Designer would imply building so many of them. The thing with business cases, and that I neglected on my first attempts, is that they put the focus on, well, “the business”.
The ultimate goal is to build a powerful response to the question “Why should WE spend money and resources on this and not on something else?” not “Why is it good?” in general.
After some trial and error, I understood a bit better how to tackle the main comments and answer this question to move the conversation forward. Of course, the business case and strategy you build will depend a lot on the specific situation of your company and your product, but here is my contribution, hoping it can help.
In this article, I will focus on two of the most common situations: pushing for accessibility in a new product or feature and starting to bring accessibility to existing products that didn’t consider it before.
Implementing accessibility has a cost. Everything in a project has a cost. If developers are solving accessibility issues, they are not working on new features, so at the very least, you have to consider the opportunity cost. You have to make sure that you transform that cost into an investment and that that investment provides good results. You need to provide some more details on how you do it, so here are the key questions that help me to build my case:
There is a good chance that your stakeholders have heard about accessibility due to the regulations. In the past years, accessibility has become a hot topic, mainly motivated by the European Accessibility Act (EAA), the Web Accessibility Directive (WAD) in Europe or the Americans with Disabilities Act (ADA), and the Section 508 of the Rehabilitation Act in the US and equivalent regulations on other countries. They should definitely be aware of them. However, unless they are from the legal department, they may not need to know every detail; just having an overview should be enough to understand the landscape. You can simplify it a bit, so no one panics.
One of the most useful slides I use is a summary table of the regulations with some key information:
In addition, explain how the WCAG relates to the regulation. In the end, it is a third-party international standard used as the baseline for most official laws and directives and comes up in conversations quite often.
Keep in mind that using the regulation to motivate your case can work, but only to some point. We are aware that the regulation about accessibility is getting stronger and the requirements are affecting a good number of companies, especially big companies, but still not everyone. If you only base your case on it, the easy answer is, “Yeah, well, but we are not required to do it”.
If we start working now we will have time to prepare. If we consider accessibility for all the new features and projects, the cost won’t be affected much, and we will be prepared for the future.
However, many companies still don’t see the urgency of working on it if they are not directly required to do so by the regulation yet, and it is not certain that they will need to do it in the future. They prefer not to focus on it until that moment arrives. It is not necessarily a problem to be prioritized now, and there may be more urgent matters.
They should be aware of the regulations and the situation. We should show them how they could be affected, but if we don’t show the real value that accessibility brings to the products and the company, the conversation may end there.
Big companies are starting to consider accessibility as part of their procurement process, which means that it is a hard requirement to become a provider, a checkbox in the selection process. You can try reaching out to your sales department to see if any clients are asking about your plans regarding accessibility compliance. If so, make sure you document them in the business case. Include some rough background research about those clients:
The potential revenue and interest from important clients can be a good motivation.
In addition, try to find out if your competitors care about accessibility or are compliant. You can go to their website and see if they have an accessibility statement, if they have any certification by external parties (normally on the footer), if they include their accessibility level on their sales materials, or just try basic keyboard navigation and run an automatic checker to see what their situation is. If none of them are compliant or their accessibility level is really low, becoming compliant or implementing accessibility may be a competitive advantage for you, a differentiator. On the other hand, if they are compliant and you are not, you may lose some deals because of it.
To sum up, check clients’ interest in the topic, compare the situation of different competitors, and see if accessibility could be a potential revenue generator.
Depending on the industries your product focuses on, the assumption may be that you don’t have a big user base of people with disabilities, and therefore, your users won’t benefit much from accessibility.
Accessibility helps everyone, and if you are reading this article, it is probably because you agree with it. But that statement sounds too generic and a bit theoretical, so it is important to provide specific and accurate examples around your users, in particular, that help people visualize it.
Think of your user base. What characteristics do they have? In which situations do they use your software? Maybe most of your users don’t have a disability, or you don’t even have the data about it, but they are office workers who use your software a lot, and having good keyboard navigation would help them to be more efficient. Maybe most of them are over fifty years old and can benefit from adapting the font size. They might have to use the software in the open air and are affected by sun glare, so they need high contrast between elements, or they have to wear gloves and prefer larger target sizes.
And I would say you always have to account for neurodiversity. The idea is to identify in which everyday situations your users face they can benefit from accessibility, even if they don’t have a disability.
Another key thing is to look for specific feedback from your users and customers on accessibility. If you are lucky enough to have an insight repository, look for anything related. Keep in mind that people can be asking about accessibility without knowing that they are asking for accessibility, so don’t expect to find all the insights directly with an “accessibility” tag, but rather search for related keywords in the “user’s vocabulary” (colors, hard to click, mobile devices, zoom, keyboard, error, and so on).
If you don’t have access to a repository, you can contact customer service and try to find out help requests or feedback about it with them. Anything you find is evidence that your users, your specific users, benefit from accessibility.
Accessibility overlaps heavily with best practices for usability, design, and development. Working on it helps us improve the overall product quality without, in some cases, adding extra effort.
In terms of design, the overlap between accessibility improvements and usability improvements is really huge. Things like writing precise error messages, having a clear page structure, relying on consistency, including clear labels and instructions, or keeping the user in control are some examples of the intersection. To visualize it, I like taking the 10 usability heuristics of Nielsen Norman and relating them to design-related success criteria from the WCAG.
For the developers, the work on accessibility creates a more structured code that is easier to understand. Some of the key aspects are the use of markup and the proper order of the code. In addition, the use of landmarks is key for managing responsive interfaces and, of course, choosing the most adequate component for the specific functionality needed and identifying it correctly with unique labels prevents the product from having unexpected behaviors.
As for the QA team, the test that they perform can vary a lot based on the product, but testing the responsiveness is normally a must, as well as keyboard navigation since it increases the efficiency of repetitive tasks.
Considering accessibility implies having clear guidelines that help you to work in the correct direction and overlap with things that we should already be doing.
As we said, we are going to focus on two of the most common situations: pushing for accessibility in a new product or feature and starting to incorporate accessibility into existing products that didn’t consider it before.
If you are about to build a product from scratch, you have a wonderful opportunity to apply an accessibility-first approach and consider accessibility by default from the very beginning. This approach allows you to minimize the number of accessibility issues that end up reaching the user and reduces the cost of rework when trying to fix them or when looking for compliance.
One of the key things you need to successfully apply this approach is considering accessibility as a shared responsibility. The opposite of an accessibility-first approach is the retroactive consideration of accessibility. When you only care for accessibility after the implementation and run an audit on the released product, you will find all the issues that accumulated. Plenty of them could have been easily solvable if you knew them when you were designing or coding, but solving them afterward becomes complicated.
For example, if you only considered drag and drop for rearranging a list of items, now you have to rethink the interaction process and make sure it works in all the cases, devices, and so on. If single-point interactions were a requirement from the beginning, you would just implement them naturally and save time.
Applying an accessibility-first approach means that everyone has to contribute.
If everyone shares the ownership and spends a bit more time on including accessibility in their task, the overall result will have a good base. Of course, you may still need to tackle some specific issues with an expert, and when auditing the final product, you will probably still find some issues that escaped the process, but the number will be drastically lower.
In addition, the process of auditing your product can get much lighter. Running an accessibility audit means first defining who will do it: is it internal or external? If it is external, which providers? How long would it take to negotiate the contract?
Afterward, you have to set the scope of the audit. It is impossible to check the full product, so you start by checking the most important workflows and key pages. Then, you will do the analysis. The result is normally a list of issues prioritized based on the user impact and some recommendations for remediating it.
Once you have the issues, you have to plan the remediation and figure out how much capacity from the teams we have to allocate to it based on when we want to have the fixes ready. You also have to group similar issues together to prevent the change of context during remediation, increase efficiency, and eliminate all duplicated issues (the auditors may not know the architecture of the product, so you may find several issues documented that, in reality, are just one because you are using the same component).
Considering this full process, for a large product, you can easily spend three months just before you start the actual remediation of the issues. Applying an accessibility-first approach means that the number of issues that reach the audit of the released product is much lower, so the process of auditing and fixing goes much faster.
If you can apply this approach, you should definitely consider the need for educational resources and their impact. You don’t want people just to work on accessibility but to understand the value they are creating when doing it (I am preparing another article that focuses on this). You want them to feel comfortable with the topic and understand what their responsibilities are and which things they have to pay attention to. Check if you already have accessibility resources inside the company that you can use. The important thing for the business is that those resources are going to contribute to reducing the effort.
The implementation of an accessibility-first approach has a very clear learning curve. In the beginning, people will take a bit of extra time to consider accessibility as part of their task, but after they have done it for several tasks, it comes naturally, and the effort needed to implement it really drops.
Think of “not relying on color only for conveying information”, as a designer, the first two times you have to figure something out instead of just changing the color of a text or icon to convey a status, you spend some time looking for solutions, afterward, you already have in mind a bunch of strategies that allow you to directly chose a valid option almost automatically.
Using an accessibility-first approach for new products is a clear strategy, but it is also valid for new features in an existing product. If you include it by default in anything new you create, you are preventing new issues from accumulating.
To sum up, applying an accessibility-first approach is really beneficial.
Considering accessibility from the beginning can help you to largely reduce the number of issues that may appear in audits after the release since it prevents the issues from accumulating, distributes the effort across the full product team, and substantially reduces the cost, as there will be less need for retroactive remediation of the issues that appear.
“
If you can implement an accessibility-first approach, do it.
If you try to bring accessibility to legacy products that have been running for many years, an accessibility-first approach may not be enough. In these cases, there are a million topics competing for priority and resources. Accessibility may be perceived as a massive effort that brings reduced value.
You may face a product that can have a big technical debt, that may not have a big user base of people with disabilities, or in which the number of existing accessibility issues is so overwhelming that you would need five years to solve them. You won´t be able to move forward if you try to solve all the problems at once. Here are some of the strategies that have worked for me to kick off the work on accessibility.
Start by checking the Design System. If the Design System has accessibility issues, they are going to be inherited by all the products that use them, so it is better to solve them at a higher level than to have each product team solving the exact same issue in all their products. You can begin by taking a quick look at it:
If you have a dedicated team for the Design System, you can also reach out to them. You can find out what is their level of awareness on the topic. If they don’t have much knowledge, you can give them an introduction or help them identify and fix the knowledge gaps they have.
If you notice some issues, you can organize a proper audit of the design system from the design and development perspective and pair up with them to fix as much as you can. It is a good way of getting some extra hands to help you while tackling strategic issues.
When working on the Design System, you can also spot which components or areas are more complex and create guidelines and documentation together with them to help the teams reuse those components and patterns, leveraging accessibility.
If the Design System is in good shape, you don’t have one, or you prefer to focus only on the product, you need to start by analyzing and fixing the most relevant part. You have to set a manageable scope. I recommend taking the most relevant workflows and the ones the users use the most. Two or three of them could be a good start. Inside the workflows, try picking the pages that have different structures so you can have a representative sample, for instance, one with a form, a table, plain text, lots of images, and so on. In many cases, the pages that share the same structure share the same problems, so having more variety in the sample helps you to pick more critical issues.
Once you have chosen the workflows and screens, you can audit them, but with a reduced scope. If your product has never considered accessibility, it is likely to have way too many issues. When doing an audit, you normally test compliance with all the success criteria (59 if we consider levels A and AA) and do manual testing with different browsers, screen readers, and devices. Then, document each of the issues, prioritize them, and include the remediation in the planning.
It takes a lot of time, and you may get hundreds of issues, or even thousands, which makes you feel like “I will never get this done” and if you even get there like “I am finally done with this I don’t want to hear about it for a long time”. If this is the situation you are forecasting for the business, most likely, you will not get the green light for the project. It is too much of an investment. So unless they have hard requirements for compliance coming from some really strategic customers, you are going to get stuck.
As we said, ideally, we would do a complete audit and fix everything, but delivering some value is better than delivering nothing, so instead, you can propose a reduced first audit to get you on the move. Rather than doing a detailed audit of all 59 criteria, I normally focus on these three things:
With these three tests, you will already have a large number of critical issues and blockers to solve while staying close to the overlapping area between accessibility and good design and development practices and not taking too much time.
Remember, the goal of this first audit is to get easy-to-identify critical issues to have a starting point, not to solve all the problems. In this way, you can start delivering value while building the idea that accessibility is not a one-time fix but a continuous process. In addition, it gives you a lot of insights into the aspects in which the teams need guidelines and training, as well as defining the minimum things that the different roles have to consider when working to reduce the number of future accessibility issues. You want to take it as a learning opportunity.
Note: Accessibility insights is a good tool for auditing by yourself as it includes explanations and visual helpers and guides you through the process.
Screen reader testing should be added to the audit scope if you can, but it can be hard to do it if you have never done it before, and some of the issues will already be highlighted during the automatic check and the keyboard testing.
The results you want to achieve are going to have a huge impact on the strategy.
Are you aiming for compliance or bringing value to the users and preparing for the future?
This is a key question you have to ask yourself.
Compliance with the regulation is pretty much a binary option. To be compliant with the WCAG at a certain level, let’s say AA, you should pass all the success criteria for that level and the previous ones. Each success criterion intends to help people with a specific disability. If you try to be compliant only with some of them, you would be leaving people out. Of course, in reality, there are always going to be some minor issues and violations of a success criterion that reach the user. But the idea is that you are either compliant or not. With this in mind, you have to make sure that you consider several audits, ideally by a certified external party that can reassure your compliance.
Trying to become compliant with a product that has never considered accessibility can become quite a large task, so it may not be the best first step. But, in general, if you are aiming for full compliance, it may be because you have strong motivations coming from the risk reduction and competitive advantage categories.
On the other hand, if your goal is to start including accessibility in the product to prepare for the future and help users, you will probably target a lighter result. Rather than looking for perfection, you want to start to have a level that is good enough as soon as possible.
Compliance is binary, but accessibility is a spectrum. You can have a pretty good level of accessibility even if you are not fully compliant.
“
You can focus on identifying and solving the most critical issues for the users and on applying an accessibility-first approach to new developments. The result is probably not compliant and not perfect, but it eliminates critical barriers without a huge effort. It will have basic accessibility to help users, and you can apply an iterative approach to improve the level.
Keep in mind that it is impossible to have a 100% accessible product. As the product evolves, there are always going to be some issues that escape the test and reach the user. The important thing is to work to ensure that these issues are minor ones and not blockers or critical ones. If you can get the resources to fix the most important problems, you are already bringing value, even if you don’t reach compliance.
An accessibility-first approach typically means you have to assign 5 to 10% of the product capacity to apply it (the number goes down to 5% due to the learning curve). The underlying risk, though, is that the business still considers these percentages to be too high. To prevent this from happening, you have to highlight strongly the side value of accessibility and the huge overlap it has with the design and development best practices we mentioned above.
In addition, to help justify the cost, you can look for examples inside your company that allow you to compare it with the cost of retroactive fitting accessibility. If there are not any, you can look for some basic issue, such as the lack of structure of a page, and use it to illustrate that in order to add the structure afterward, once the product is released you would need to do a substantial rework or ask a developer to help you to estimate the effort of adding a heading structure to 40 different pages after released.
As for introducing accessibility in existing products, the cost can be quite hard to estimate. Having a rough audit can help you understand how many critical issues you have at the start, and you can ask developers to help you estimate some of the changes to get a rough idea.
The most interesting approach that helps you to reduce the “cost of accessibility” is exploiting the overlap between accessibility and usability or product features.
“
If you attach accessibility improvements to usability or UX ones, then it doesn’t really need dedicated capacity. For example, if some of the inputs are lacking labels or instructions and your users get confused, it is a usability problem that overlaps with accessibility. Normally, accessibility issues related to the Reflow criteria are quite time-consuming, as they rely on a proper responsive design. But isn’t it just good design?
I recommend checking the list of features in the product backlog and the feedback from the users to find out which accessibility improvements can you combine with them, especially with features that have priority according to the product strategy (such us, enabling the product on mobile devices, or improving efficiency by promoting keyboard navigation).
The bigger the overlap, the more you can reduce the effort. This said, I would say it is better not to make it too ambitious when you are starting. It is better to start moving, even if it is slowly, than to hit a wall. When you manage to start with it, you will spark curiosity in other people, gain allies, and have results that can help you to expand the project and the scope.
You can also consider an alternative approach, define an affordable capacity that you could dedicate based on your product situation (maybe 10 or 15%), and set the scope to match it.
Finally, it is also important to gather the existing resources you have access to, internal or external. If there are guidelines, if the Design System is accessible, if there are related company goals, educational sessions… Whatever is there already is something you can use, and that doesn’t add to the total cost of the project. If the Design System is accessible, it would be a waste if we don’t leverage it and make sure we implement the components in an accessible way. You can put together an overview to show the support you have.
Business stakeholders are short on time and have many things in mind. If you want them to make a decision and consider all the factors when making it, you have to help them visualize them together in an executive summary.
If there is a single direction that you are trying to promote, for example, implementing an accessibility-first approach for new products and features, you can put on a slide the three key questions we mentioned above and the answers to those questions:
If there are different directions you can take, for example, you want to start to incorporate accessibility into products that meet certain conditions, or you can afford different capacities dedicated to accessibility for different products, you can use a decision-making diagram or a decision-making matrix. The idea is to visualize the different criteria that can affect the strategy and the adapted result for each of them.
For example,
Mapping out the factors and possible directions can help you and decision-makers understand which products can be a better starting point for accessibility, where it makes sense to allocate more capacity, and which possibilities are open. This becomes especially relevant when you are trying to bring accessibility to several products at the same time.
Whatever representation you choose for your conditions, make sure it visualizes the answers to those questions to facilitate the decision-making process and get approval. I generally include it at the end of the presentation, or even at the beginning and the end.
Even if your business case is really good, sometimes you don’t get to have a big impact due to circumstances. It may be that there is a big shift in priorities, that the stakeholders change, that your contract ends (if you are a consultant), or that the company just doesn’t have the resources to work on it at that moment, and it gets postponed.
I know it can be very frustrating, but don´t lose the motivation. Change can move quite slowly, especially in big companies, but if you have put the topic into people’s minds, it will be back on the table. In the meantime, you can try organizing evangelization sessions for the teams to find new allies and share your passion. You may need to wait a bit more, but there will be more opportunities to push the topic again, and since people already know about it, you will probably get more support. You have initiated the change, and your effort will not be lost.
The Importance Of Graceful Degradation In Accessible Interface Design The Importance Of Graceful Degradation In Accessible Interface Design Eleanor Hecks 2024-12-06T09:00:00+00:00 2025-06-25T15:04:30+00:00 Graceful degradation is a design approach that ensures the basics of a website will still function even if specific individual parts of it […]
Accessibility
2024-12-06T09:00:00+00:00
2025-06-25T15:04:30+00:00
Graceful degradation is a design approach that ensures the basics of a website will still function even if specific individual parts of it stop working. The approach removes single points of failure: just because one thing stops working doesn’t mean the system as a whole fails. A site following this principle fails in pieces instead of all at once, so the most important features remain available when some components encounter an error.
The idea or the concept of single points of failure is well known in the manufacturing sector. It’s one of the most common resilience strategies in manufacturing and supply chain operations. A factory with multiple sources of material can keep working even when one supplier becomes unavailable. However, it’s become increasingly crucial to web development as user expectations around availability and functionality rise.
Data center redundancy is a common example of graceful degradation in web development. By using multiple server components, websites ensure they’ll stay up when one or more servers fail. In a design context, it may look like guaranteeing the lack of support for a given feature in a user’s browser or device doesn’t render an app unusable.
Escalators are a familiar real-world example of the same concept. When they stop working, they can still get people from one floor to the next by acting as stairs. They may not be as functional as they normally are, but they’re not entirely useless.
The BBC News webpage is a good example of graceful degradation in web design. As this screenshot shows, the site prioritizes loading navigation and the text within a news story over images. Consequently, slow speeds or old, incompatible browser plugins may make pictures unavailable, but the site’s core function — sharing the news — is still accessible.
In contrast, the Adobe Express website is an example of what happens without graceful degradation. Instead of making some features unavailable or dropping load times, the entire site is inaccessible on some browsers. Consequently, users have to update or switch software to use the web app, which isn’t great for accessibility.
The graceful degradation approach acts as the opposite of progressive enhancement — an approach in which a designer builds the basics of a website and progressively adds features that are turned on only if a browser is capable of running them. Each layer of features is turned off by default, allowing for one seamless user experience designed to work for everyone.
There is much debate between designers about whether graceful degradation or progressive enhancement is the best way to build site functionality. In reality, though, both are important. Each method has unique pros and cons, so the two can complement each other to provide the most resilience.
Progressive enhancement is a good strategy when building a new site or app because you ensure a functional experience for everyone from the start. However, new standards and issues can emerge in the future, which is where graceful degradation comes in. This approach helps you adjust an existing website to comply with new accessibility standards or resolve a compatibility problem you didn’t notice earlier.
Focusing solely on one design principle or the other will limit accessibility. Progressive enhancement alone struggles to account for post-launch functionality issues, while graceful degradation alone may fail to provide the most feature-rich baseline experience. Combining both will produce the best result.
“
Ensuring your site or app remains functional is crucial for accessibility. When core functions become unavailable, the platform is no longer accessible to anyone. On a smaller scale, if features like text-to-speech readers or video closed captioning stop working, users with sight difficulties may be unable to enjoy the site.
Graceful degradation’s impact on accessibility is all the larger when considering varying device capabilities. As the average person spends 3.6 hours each day on their phone, failing to ensure a site supports less powerful mobile browsers will alienate a considerable chunk of your audience. Even if some complex functions may not work on mobile, sacrificing those to keep the bulk of the website available on phones ensures broader accessibility.
Outdated browsers are another common accessibility issue you can address with graceful degradation. Consider this example from Fairleigh Dickinson University about Adobe Flash, which most modern browsers no longer support.
Software still using Flash cannot use the multi-factor authentication feature in question. As a result, users with older programs can’t log in. Graceful degradation may compromise by making some functionality unavailable to Flash-supporting browsers while still allowing general access. That way, people don’t need to upgrade to use the service.
Graceful degradation removes technological barriers to accessibility. In a broader sense, it also keeps your site or app running at all times, even amid unforeseen technical difficulties. While there are many ways you can achieve that, here are some general best practices to follow.
The first step in ensuring graceful degradation is determining what your core functions are. You can only guarantee the availability of mission-critical features once you know what’s essential and what isn’t.
Review your user data to see what your audience interacts with most — these are generally elements worth prioritizing. Anything related to site security, transactions, and readability is also crucial. Infrequently used features or elements like video players and interactive maps are nice to have but okay to sacrifice if you must to ensure mission-critical components remain available.
Once you’ve categorized site functions by criticality, you can ensure redundancy for the most important ones. That may mean replicating elements in a few forms to work on varying browsers or devices. Alternatively, you could provide multiple services to carry out important functions, like supporting alternate payment methods or providing both video and text versions of content.
Remember that redundancy applies to the hardware your platform runs on, too. The Uptime Institute classifies data centers into tiers, which you can use to determine what redundant systems you need. Similarly, make sure you can run your site on multiple servers to avoid a crash should one go down.
Remember that graceful degradation is also about supporting software and hardware of varying capabilities. One of the most important considerations under that umbrella for web design is to accommodate outdated browsers.
While mobile devices don’t support Flash, some older versions of desktop browsers still use it. You can work with both by avoiding Flash — you can often use HTML5 instead — but not requiring users to have a non-Flash-supporting browser. Similarly, you can offer low-bandwidth, simple alternatives to any features that take up considerable processing power to keep things accessible on older systems.
Remember to pay attention to newer software’s security settings, too. Error messages like this one a Microsoft user posted about can appear if a site does not support some browsers’ updated security protocols. Always keep up with updates from popular platforms like Chrome and Safari to meet these standards and avoid such access issues.
Load balancing is another crucial step in graceful degradation. Many cloud services automatically distribute traffic between server resources to prevent overloading. Enabling this also ensures that requests can be processed on a different part of the system if another fails.
Caching is similar. By storing critical data, you build a fallback plan if an external service or application program interface (API) doesn’t work. When the API doesn’t respond, you can load the cached data instead. As a result, caches significantly reduce latency in many cases, but you should be aware that you can’t cache everything. Focus on the most critical functions.
Finally, be sure to test your website for accessibility issues before taking it live. Access it from multiple devices, including various browser versions. See if you can run it on a single server to test its ability to balance loads.
You likely won’t discover all possible errors in testing, but it’s better to catch some than none. Remember to test your site’s functionality before any updates or redesigns, too.
Designers, both big and small, can start their graceful degradation journey by tweaking some settings with their web hosting service. AWS offers guidance for managing failures you can use to build degradation into your site’s architecture. Hosting providers should also allow you to upgrade your storage plan and configure your server settings to provide redundancy and balance loads.
Businesses large enough to run their own data centers should install redundant server capacity and uninterruptible power supplies to keep things running. Smaller organizations can instead rely on their code, using semantic HTML to keep it simple enough for multiple browsers. Programming nonessential things like images and videos to stop when bandwidth is low will also help.
Virtualization systems like Kubernetes are also useful as a way to scale site capacity and help load elements separately from one another to maintain accessibility. Testing tools like BrowserStack, WAVE, and CSS HTML Validator can assist you by revealing if your site has functional issues on some browsers or for certain users.
At its core, web accessibility is about ensuring a platform works as intended for all people. While design features may be the most obvious part of that goal, technical defenses also play a role. A site is only accessible when it works, so you must keep it functional, even when unexpected hiccups occur.
Graceful degradation is not a perfect solution, but it prevents a small issue from becoming a larger one. Following these five steps to implement it on your website or app will ensure that your work in creating an accessible design doesn’t go to waste.
Creating An Effective Multistep Form For Better User Experience Creating An Effective Multistep Form For Better User Experience Amejimaobari Ollornwi 2024-12-03T10:00:00+00:00 2025-06-25T15:04:30+00:00 For a multistep form, planning involves structuring questions logically across steps, grouping similar questions, and minimizing the number of steps and the amount […]
Accessibility
2024-12-03T10:00:00+00:00
2025-06-25T15:04:30+00:00
For a multistep form, planning involves structuring questions logically across steps, grouping similar questions, and minimizing the number of steps and the amount of required information for each step. Whatever makes each step focused and manageable is what should be aimed for.
In this tutorial, we will create a multistep form for a job application. Here are the details we are going to be requesting from the applicant at each step:
You can think of structuring these questions as a digital way of getting to know somebody. You can’t meet someone for the first time and ask them about their work experience without first asking for their name.
Based on the steps we have above, this is what the body of our HTML with our form should look like. First, the main <form>
element:
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<!-- Step 2: Work Experience -->
<!-- Step 3: Skills & Qualifications -->
<!-- Step 4: Review & Submit -->
</form>
Step 1 is for filling in personal information, like the applicant’s name, email address, and phone number:
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<fieldset class="step" id="step-1">
<legend id="step1Label">Step 1: Personal Information</legend>
<label for="name">Full Name</label>
<input type="text" id="name" name="name" required />
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required />
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" required />
</fieldset>
<!-- Step 2: Work Experience -->
<!-- Step 3: Skills & Qualifications -->
<!-- Step 4: Review & Submit -->
</form>
Once the applicant completes the first step, we’ll navigate them to Step 2, focusing on their work experience so that we can collect information like their most recent company, job title, and years of experience. We’ll tack on a new <fieldset>
with those inputs:
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<!-- Step 2: Work Experience -->
<fieldset class="step" id="step-2" hidden>
<legend id="step2Label">Step 2: Work Experience</legend>
<label for="company">Most Recent Company</label>
<input type="text" id="company" name="company" required />
<label for="jobTitle">Job Title</label>
<input type="text" id="jobTitle" name="jobTitle" required />
<label for="yearsExperience">Years of Experience</label>
<input
type="number"
id="yearsExperience"
name="yearsExperience"
min="0"
required
/>
</fieldset>
<!-- Step 3: Skills & Qualifications -->
<!-- Step 4: Review & Submit -->
</form>
Step 3 is all about the applicant listing their skills and qualifications for the job they’re applying for:
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<!-- Step 2: Work Experience -->
<!-- Step 3: Skills & Qualifications -->
<fieldset class="step" id="step-3" hidden>
<legend id="step3Label">Step 3: Skills & Qualifications</legend>
<label for="skills">Skill(s)</label>
<textarea id="skills" name="skills" rows="4" required></textarea>
<label for="highestDegree">Degree Obtained (Highest)</label>
<select id="highestDegree" name="highestDegree" required>
<option value="">Select Degree</option>
<option value="highschool">High School Diploma</option>
<option value="bachelor">Bachelor's Degree</option>
<option value="master">Master's Degree</option>
<option value="phd">Ph.D.</option>
</select>
</fieldset>
<!-- Step 4: Review & Submit -->
<fieldset class="step" id="step-4" hidden>
<legend id="step4Label">Step 4: Review & Submit</legend>
<p>Review your information before submitting the application.</p>
<button type="submit">Submit Application</button>
</fieldset>
</form>
And, finally, we’ll allow the applicant to review their information before submitting it:
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<!-- Step 2: Work Experience -->
<!-- Step 3: Skills & Qualifications -->
<!-- Step 4: Review & Submit -->
<fieldset class="step" id="step-4" hidden>
<legend id="step4Label">Step 4: Review & Submit</legend>
<p>Review your information before submitting the application.</p>
<button type="submit">Submit Application</button>
</fieldset>
</form>
Notice: We’ve added a hidden
attribute to every fieldset
element but the first one. This ensures that the user sees only the first step. Once they are done with the first step, they can proceed to fill out their work experience on the second step by clicking a navigational button. We’ll add this button later on.
To keep things focused, we’re not going to be emphasizing the styles in this tutorial. What we’ll do to keep things simple is leverage the Simple.css style framework to get the form in good shape for the rest of the tutorial.
If you’re following along, we can include Simple’s styles in the document <head>
:
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css" />
And from there, go ahead and create a style.css
file with the following styles that I’ve folded up.
<details>
<summary>View CSS</summary>
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
main {
padding: 0 30px;
}
h1 {
font-size: 1.8rem;
text-align: center;
}
.stepper {
display: flex;
justify-content: flex-end;
padding-right: 10px;
}
form {
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.2);
padding: 12px;
}
input,
textarea,
select {
outline: none;
}
input:valid,
textarea:valid,
select:valid,
input:focus:valid,
textarea:focus:valid,
select:focus:valid {
border-color: green;
}
input:focus:invalid,
textarea:focus:invalid,
select:focus:invalid {
border: 1px solid red;
}
</details>
An easy way to ruin the user experience for a multi-step form is to wait until the user gets to the last step in the form before letting them know of any error they made along the way. Each step of the form should be validated for errors before moving on to the next step, and descriptive error messages should be displayed to enable users to understand what is wrong and how to fix it.
Now, the only part of our form that is visible is the first step. To complete the form, users need to be able to navigate to the other steps. We are going to use several buttons to pull this off. The first step is going to have a Next button. The second and third steps are going to have both a Previous and a Next button, and the fourth step is going to have a Previous and a Submit button.
<form id="jobApplicationForm">
<!-- Step 1: Personal Information -->
<fieldset>
<!-- ... -->
<button type="button" class="next" onclick="nextStep()">Next</button>
</fieldset>
<!-- Step 2: Work Experience -->
<fieldset>
<!-- ... -->
<button type="button" class="previous" onclick="previousStep()">Previous</button>
<button type="button" class="next" onclick="nextStep()">Next</button>
</fieldset>
<!-- Step 3: Skills & Qualifications -->
<fieldset>
<!-- ... -->
<button type="button" class="previous" onclick="previousStep()">Previous</button>
<button type="button" class="next" onclick="nextStep()">Next</button>
</fieldset>
<!-- Step 4: Review & Submit -->
<fieldset>
<!-- ... -->
<button type="button" class="previous" onclick="previousStep()">Previous</button>
<button type="submit">Submit Application</button>
</fieldset>
</form>
Notice: We’ve added onclick
attributes to the Previous and Next buttons to link them to their respective JavaScript functions: previousStep()
and nextStep()
.
The nextStep()
function is linked to the Next button. Whenever the user clicks the Next button, the nextStep()
function will first check to ensure that all the fields for whatever step the user is on have been filled out correctly before moving on to the next step. If the fields haven’t been filled correctly, it displays some error messages, letting the user know that they’ve done something wrong and informing them what to do to make the errors go away.
Before we go into the implementation of the nextStep
function, there are certain variables we need to define because they will be needed in the function. First, we need the input fields from the DOM so we can run checks on them to make sure they are valid.
// Step 1 fields
const name = document.getElementById("name");
const email = document.getElementById("email");
const phone = document.getElementById("phone");
// Step 2 fields
const company = document.getElementById("company");
const jobTitle = document.getElementById("jobTitle");
const yearsExperience = document.getElementById("yearsExperience");
// Step 3 fields
const skills = document.getElementById("skills");
const highestDegree = document.getElementById("highestDegree");
Then, we’re going to need an array to store our error messages.
let errorMsgs = [];
Also, we would need an element in the DOM where we can insert those error messages after they’ve been generated. This element should be placed in the HTML just below the last fieldset
closing tag:
<div id="errorMessages" style="color: rgb(253, 67, 67)"></div>
Add the above div
to the JavaScript code using the following line:
const errorMessagesDiv = document.getElementById("errorMessages");
And finally, we need a variable to keep track of the current step.
let currentStep = 1;
Now that we have all our variables in place, here’s the implementation of the nextstep()
function:
function nextStep() {
errorMsgs = [];
errorMessagesDiv.innerText = "";
switch (currentStep) {
case 1:
addValidationErrors(name, email, phone);
validateStep(errorMsgs);
break;
case 2:
addValidationErrors(company, jobTitle, yearsExperience);
validateStep(errorMsgs);
break;
case 3:
addValidationErrors(skills, highestDegree);
validateStep(errorMsgs);
break;
}
}
The moment the Next button is pressed, our code first checks which step the user is currently on, and based on this information, it validates the data for that specific step by calling the addValidationErrors()
function. If there are errors, we display them. Then, the form calls the validateStep()
function to verify that there are no errors before moving on to the next step. If there are errors, it prevents the user from going on to the next step.
Whenever the nextStep()
function runs, the error messages are cleared first to avoid appending errors from a different step to existing errors or re-adding existing error messages when the addValidationErrors
function runs. The addValidationErrors
function is called for each step using the fields for that step as arguments.
Here’s how the addValidationErrors
function is implemented:
function addValidationErrors(fieldOne, fieldTwo, fieldThree = undefined) {
if (!fieldOne.checkValidity()) {
const label = document.querySelector(`label[for="${fieldOne.id}"]`);
errorMsgs.push(`Please Enter A Valid ${label.textContent}`);
}
if (!fieldTwo.checkValidity()) {
const label = document.querySelector(`label[for="${fieldTwo.id}"]`);
errorMsgs.push(`Please Enter A Valid ${label.textContent}`);
}
if (fieldThree && !fieldThree.checkValidity()) {
const label = document.querySelector(`label[for="${fieldThree.id}"]`);
errorMsgs.push(`Please Enter A Valid ${label.textContent}`);
}
if (errorMsgs.length > 0) {
errorMessagesDiv.innerText = errorMsgs.join("n");
}
}
This is how the validateStep()
function is defined:
function validateStep(errorMsgs) {
if (errorMsgs.length === 0) {
showStep(currentStep + 1);
}
}
The validateStep()
function checks for errors. If there are none, it proceeds to the next step with the help of the showStep()
function.
function showStep(step) {
steps.forEach((el, index) => {
el.hidden = index + 1 !== step;
});
currentStep = step;
}
The showStep()
function requires the four fieldsets in the DOM. Add the following line to the top of the JavaScript code to make the fieldsets available:
const steps = document.querySelectorAll(".step");
What the showStep()
function does is to go through all the fieldsets
in our form and hide whatever fieldset
is not equal to the one we’re navigating to. Then, it updates the currentStep
variable to be equal to the step we’re navigating to.
The previousStep()
function is linked to the Previous button. Whenever the previous button is clicked, similarly to the nextStep
function, the error messages are also cleared from the page, and navigation is also handled by the showStep
function.
function previousStep() {
errorMessagesDiv.innerText = "";
showStep(currentStep - 1);
}
Whenever the showStep()
function is called with “currentStep - 1
” as an argument (as in this case), we go back to the previous step, while moving to the next step happens by calling the showStep()
function with “currentStep + 1
” as an argument (as in the case of the validateStep()
function).
One other way of improving the user experience for a multi-step form, is by integrating visual cues, things that will give users feedback on the process they are on. These things can include a progress indicator or a stepper to help the user know the exact step they are on.
To integrate a stepper into our form (sort of like this one from Material Design), the first thing we need to do is add it to the HTML just below the opening <form>
tag.
<form id="jobApplicationForm">
<div class="stepper">
<span><span class="currentStep">1</span>/4</span>
</div>
<!-- ... -->
</form>
Next, we need to query the part of the stepper that will represent the current step. This is the span tag with the class name of currentStep
.
const currentStepDiv = document.querySelector(".currentStep");
Now, we need to update the stepper value whenever the previous or next buttons are clicked. To do this, we need to update the showStep()
function by appending the following line to it:
currentStepDiv.innerText = currentStep;
This line is added to the showStep()
function because the showStep()
function is responsible for navigating between steps and updating the currentStep
variable. So, whenever the currentStep
variable is updated, the currentStepDiv should also be updated to reflect that change.
One major way we can improve the form’s user experience is by storing user data in the browser. Multistep forms are usually long and require users to enter a lot of information about themselves. Imagine a user filling out 95% of a form, then accidentally hitting the F5 button on their keyboard and losing all their progress. That would be a really bad experience for the user.
Using localStorage
, we can store user information as soon as it is entered and retrieve it as soon as the DOM content is loaded, so users can always continue filling out their forms from wherever they left off. To add this feature to our forms, we can begin by saving the user’s information as soon as it is typed. This can be achieved using the input
event.
Before adding the input
event listener, get the form element from the DOM:
const form = document.getElementById("jobApplicationForm");
Now we can add the input
event listener:
// Save data on each input event
form.addEventListener("input", () => {
const formData = {
name: document.getElementById("name").value,
email: document.getElementById("email").value,
phone: document.getElementById("phone").value,
company: document.getElementById("company").value,
jobTitle: document.getElementById("jobTitle").value,
yearsExperience: document.getElementById("yearsExperience").value,
skills: document.getElementById("skills").value,
highestDegree: document.getElementById("highestDegree").value,
};
localStorage.setItem("formData", JSON.stringify(formData));
});
Next, we need to add some code to help us retrieve the user data once the DOM content is loaded.
window.addEventListener("DOMContentLoaded", () => {
const savedData = JSON.parse(localStorage.getItem("formData"));
if (savedData) {
document.getElementById("name").value = savedData.name || "";
document.getElementById("email").value = savedData.email || "";
document.getElementById("phone").value = savedData.phone || "";
document.getElementById("company").value = savedData.company || "";
document.getElementById("jobTitle").value = savedData.jobTitle || "";
document.getElementById("yearsExperience").value = savedData.yearsExperience || "";
document.getElementById("skills").value = savedData.skills || "";
document.getElementById("highestDegree").value = savedData.highestDegree || "";
}
});
Lastly, it is good practice to remove data from localStorage
as soon as it is no longer needed:
// Clear data on form submit
form.addEventListener('submit', () => {
// Clear localStorage once the form is submitted
localStorage.removeItem('formData');
});
localStorage
If the user accidentally closes their browser, they should be able to return to wherever they left off. This means that the current step value also has to be saved in localStorage
.
To save this value, append the following line to the showStep()
function:
localStorage.setItem("storedStep", currentStep);
Now we can retrieve the current step value and return users to wherever they left off whenever the DOM content loads. Add the following code to the DOMContentLoaded
handler to do so:
const storedStep = localStorage.getItem("storedStep");
if (storedStep) {
const storedStepInt = parseInt(storedStep);
steps.forEach((el, index) => {
el.hidden = index + 1 !== storedStepInt;
});
currentStep = storedStepInt;
currentStepDiv.innerText = currentStep;
}
Also, do not forget to clear the current step value from localStorage
when the form is submitted.
localStorage.removeItem("storedStep");
The above line should be added to the submit handler.
Creating multi-step forms can help improve user experience for complex data entry. By carefully planning out steps, implementing form validation at each step, and temporarily storing user data in the browser, you make it easier for users to complete long forms.
For the full implementation of this multi-step form, you can access the complete code on GitHub.
Build A Static RSS Reader To Fight Your Inner FOMO Build A Static RSS Reader To Fight Your Inner FOMO Karin Hendrikse 2024-10-07T13:00:00+00:00 2025-06-25T15:04:30+00:00 In a fast-paced industry like tech, it can be hard to deal with the fear of missing out on important news. […]
Accessibility
2024-10-07T13:00:00+00:00
2025-06-25T15:04:30+00:00
In a fast-paced industry like tech, it can be hard to deal with the fear of missing out on important news. But, as many of us know, there’s an absolutely huge amount of information coming in daily, and finding the right time and balance to keep up can be difficult, if not stressful. A classic piece of technology like an RSS feed is a delightful way of taking back ownership of our own time. In this article, we will create a static Really Simple Syndication (RSS) reader that will bring you the latest curated news only once (yes: once) a day.
We’ll obviously work with RSS technology in the process, but we’re also going to combine it with some things that maybe you haven’t tried before, including Astro (the static site framework), TypeScript (for JavaScript goodies), a package called rss-parser (for connecting things together), as well as scheduled functions and build hooks provided by Netlify (although there are other services that do this).
I chose these technologies purely because I really, really enjoy them! There may be other solutions out there that are more performant, come with more features, or are simply more comfortable to you — and in those cases, I encourage you to swap in whatever you’d like. The most important thing is getting the end result!
Here’s how this will go. Astro generates the website. I made the intentional decision to use a static site because I want the different RSS feeds to be fetched only once during build time, and that’s something we can control each time the site is “rebuilt” and redeployed with updates. That’s where Netlify’s scheduled functions come into play, as they let us trigger rebuilds automatically at specific times. There is no need to manually check for updates and deploy them! Cron jobs can just as readily do this if you prefer a server-side solution.
During the triggered rebuild, we’ll let the rss-parser package do exactly what it says it does: parse a list of RSS feeds that are contained in an array. The package also allows us to set a filter for the fetched results so that we only get ones from the past day, week, and so on. Personally, I only render the news from the last seven days to prevent content overload. We’ll get there!
But first…
RSS is a web feed technology that you can feed into a reader or news aggregator. Because RSS is standardized, you know what to expect when it comes to the feed’s format. That means we have a ton of fun possibilities when it comes to handling the data that the feed provides. Most news websites have their own RSS feed that you can subscribe to (this is Smashing Magazine’s RSS feed: https://www.smashingmagazine.com/feed/). An RSS feed is capable of updating every time a site publishes new content, which means it can be a quick source of the latest news, but we can tailor that frequency as well.
RSS feeds are written in an Extensible Markup Language (XML) format and have specific elements that can be used within it. Instead of focusing too much on the technicalities here, I’ll give you a link to the RSS specification. Don’t worry; that page should be scannable enough for you to find the most pertinent information you need, like the kinds of elements that are supported and what they represent. For this tutorial, we’re only using the following elements: <title>
, <link>
, <description>
, <item>
, and <pubDate>
. We’ll also let our RSS parser package do some of the work for us.
We’ll start by creating our Astro site! In your terminal run pnpm create astro@latest
. You can use any package manager you want — I’m simply trying out pnpm for myself.
After running the command, Astro’s chat-based helper, Houston, walks through some setup questions to get things started.
astro Launch sequence initiated.
dir Where should we create your new project?
./rss-buddy
tmpl How would you like to start your new project?
Include sample files
ts Do you plan to write TypeScript?
Yes
use How strict should TypeScript be?
Strict
deps Install dependencies?
Yes
git Initialize a new git repository?
Yes
I like to use Astro’s sample files so I can get started quickly, but we’re going to clean them up a bit in the process. Let’s clean up the src/pages/index.astro
file by removing everything inside of the <main></main>
tags. Then we’re good to go!
From there, we can spin things by running pnpm start
. Your terminal will tell you which localhost address you can find your site at.
The src/pages/index.astro
file is where we will make an array of RSS feeds we want to follow. We will be using Astro’s template syntax, so between the two code fences (—), create an array of feedSources
and add some feeds. If you need inspiration, you can copy this:
const feedSources = [
'https://www.smashingmagazine.com/feed/',
'https://developer.mozilla.org/en-US/blog/rss.xml',
// etc.
]
Now we’ll install the rss-parser package in our project by running pnpm install rss-parser
. This package is a small library that turns the XML that we get from fetching an RSS feed into JavaScript objects. This makes it easy for us to read our RSS feeds and manipulate the data any way we want.
Once the package is installed, open the src/pages/index.astro
file, and at the top, we’ll import the rss-parser and instantiate the Partner
class.
import Parser from 'rss-parser';
const parser = new Parser();
We use this parser to read our RSS feeds and (surprise!) parse them to JavaScript. We’re going to be dealing with a list of promises here. Normally, I would probably use Promise.all()
, but the thing is, this is supposed to be a complicated experience. If one of the feeds doesn’t work for some reason, I’d prefer to simply ignore it.
Why? Well, because Promise.all()
rejects everything even if only one of its promises is rejected. That might mean that if one feed doesn’t behave the way I’d expect it to, my entire page would be blank when I grab my hot beverage to read the news in the morning. I do not want to start my day confronted by an error.
Instead, I’ll opt to use Promise.allSettled()
. This method will actually let all promises complete even if one of them fails. In our case, this means any feed that errors will just be ignored, which is perfect.
Let’s add this to the src/pages/index.astro
file:
interface FeedItem {
feed?: string;
title?: string;
link?: string;
date?: Date;
}
const feedItems: FeedItem[] = [];
await Promise.allSettled(
feedSources.map(async (source) => {
try {
const feed = await parser.parseURL(source);
feed.items.forEach((item) => {
const date = item.pubDate ? new Date(item.pubDate) : undefined;
feedItems.push({
feed: feed.title,
title: item.title,
link: item.link,
date,
});
});
} catch (error) {
console.error(`Error fetching feed from ${source}:`, error);
}
})
);
This creates an array (or more) named feedItems
. For each URL in the feedSources
array we created earlier, the rss-parser retrieves the items and, yes, parses them into JavaScript. Then, we return whatever data we want! We’ll keep it simple for now and only return the following:
The next step is to ensure that all items are sorted by date so we’ll truly get the “latest” news. Add this small piece of code to our work:
const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());
Oh, and… remember when I said I didn’t want this RSS reader to render anything older than seven days? Let’s tackle that right now since we’re already in this code.
We’ll make a new variable called sevenDaysAgo
and assign it a date. We’ll then set that date to seven days ago and use that logic before we add a new item to our feedItems
array.
This is what the src/pages/index.astro
file should now look like at this point:
---
import Layout from '../layouts/Layout.astro';
import Parser from 'rss-parser';
const parser = new Parser();
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const feedSources = [
'https://www.smashingmagazine.com/feed/',
'https://developer.mozilla.org/en-US/blog/rss.xml',
]
interface FeedItem {
feed?: string;
title?: string;
link?: string;
date?: Date;
}
const feedItems: FeedItem[] = [];
await Promise.allSettled(
feedSources.map(async (source) => {
try {
const feed = await parser.parseURL(source);
feed.items.forEach((item) => {
const date = item.pubDate ? new Date(item.pubDate) : undefined;
if (date && date >= sevenDaysAgo) {
feedItems.push({
feed: feed.title,
title: item.title,
link: item.link,
date,
});
}
});
} catch (error) {
console.error(`Error fetching feed from ${source}:`, error);
}
})
);
const sortedFeedItems = feedItems.sort((a, b) => (b.date ?? new Date()).getTime() - (a.date ?? new Date()).getTime());
---
<Layout title="Welcome to Astro.">
<main>
</main>
</Layout>
It’s time to show our news articles on the Astro site! To keep this simple, we’ll format the items in an unordered list rather than some other fancy layout.
All we need to do is update the <Layout>
element in the file with the XML objects sprinkled in for a feed item’s title, URL, and publish date.
<Layout title="Welcome to Astro.">
<main>
{sortedFeedItems.map(item => (
<ul>
<li>
<a href={item.link}>{item.title}</a>
<p>{item.feed}</p>
<p>{item.date}</p>
</li>
</ul>
))}
</main>
</Layout>
Go ahead and run pnpm start
from the terminal. The page should display an unordered list of feed items. Of course, everything is styled at the moment, but luckily for you, you can make it look exactly like you want with CSS!
And remember that there are even more fields available in the XML for each item if you want to display more information. If you run the following snippet in your DevTools console, you’ll see all of the fields you have at your disposal:
feed.items.forEach(item => {}
We’re nearly done! The feeds are being fetched, and they are returning data back to us in JavaScript for use in our Astro page template. Since feeds are updated whenever new content is published, we need a way to fetch the latest items from it.
We want to avoid doing any of this manually. So, let’s set this site on Netlify to gain access to their scheduled functions that trigger a rebuild and their build hooks that do the building. Again, other services do this, and you’re welcome to roll this work with another provider — I’m just partial to Netlify since I work there. In any case, you can follow Netlify’s documentation for setting up a new site.
Once your site is hosted and live, you are ready to schedule your rebuilds. A build hook gives you a URL to use to trigger the new build, looking something like this:
https://api.netlify.com/build_hooks/your-build-hook-id
Let’s trigger builds every day at midnight. We’ll use Netlify’s scheduled functions. That’s really why I’m using Netlify to host this in the first place. Having them at the ready via the host greatly simplifies things since there’s no server work or complicated configurations to get this going. Set it and forget it!
We’ll install @netlify/functions
(instructions) to the project and then create the following file in the project’s root directory: netlify/functions/deploy.ts
.
This is what we want to add to that file:
// netlify/functions/deploy.ts
import type { Config } from '@netlify/functions';
const BUILD_HOOK =
'https://api.netlify.com/build_hooks/your-build-hook-id'; // replace me!
export default async (req: Request) => {
await fetch(BUILD_HOOK, {
method: 'POST',
})
};
export const config: Config = {
schedule: '0 0 * * *',
};
If you commit your code and push it, your site should re-deploy automatically. From that point on, it follows a schedule that rebuilds the site every day at midnight, ready for you to take your morning brew and catch up on everything that you think is important.
How A Bottom-Up Design Approach Enhances Site Accessibility How A Bottom-Up Design Approach Enhances Site Accessibility Eleanor Hecks 2024-10-04T09:00:00+00:00 2025-06-25T15:04:30+00:00 Accessibility is key in modern web design. A site that doesn’t consider how its user experience may differ for various audiences — especially those with […]
Accessibility
2024-10-04T09:00:00+00:00
2025-06-25T15:04:30+00:00
Accessibility is key in modern web design. A site that doesn’t consider how its user experience may differ for various audiences — especially those with disabilities — will fail to engage and serve everyone equally. One of the best ways to prevent this is to approach your site from a bottom-up perspective.
Conventional, top-down design approaches start with the big picture before breaking these goals and concepts into smaller details. Bottom-up philosophies, by contrast, consider the minute details first, eventually achieving the broader goal piece by piece.
This alternative way of thinking is important for accessibility in general because it reflects how neurodivergent people commonly think. While non-autistic people tend to think from a top-down perspective, those with autism often employ a bottom-up way of thinking.
Of course, there is considerable variation, and researchers have identified at least three specialist thinking types within the autism spectrum:
Still, research shows that people with autism and ADHD show a bias toward bottom-up thinking rather than the top-down approach you often see in neurotypical users. Consequently, a top-down strategy means you may miss details your audience may notice, and your site may not feel easily usable for all users.
As a real-world example, consider the task of writing an essay. Many students are instructed to start an essay assignment by thinking about the main point they want to convey and then create an outline with points that support the main argument. This is top-down thinking — starting with the big picture of the topic and then gradually breaking down the topic into points and then later into words that articulate these points.
In contrast, someone who uses a bottom-up thinking approach might start an essay with no outline but rather just by freely jotting down every idea that comes to mind as it comes to mind — perhaps starting with one particular idea or example that the writer finds interesting and wants to explore further and branching off from there. Then, once all the ideas have been written out, the writer goes back to group related ideas together and arrange them into a logical outline. This writer starts with the small details of the essay and then works these details into the big picture of the final form.
In web design, in particular, a bottom-up approach means starting with the specifics of the user experience instead of the desired effect. You may determine a readable layout for a single blog post, then ask how that page relates to others and slowly build on these decisions until you have several well-organized website categories.
You may even get more granular. Say you start your site design by placing a menu at the bottom of a mobile site to make it easier to tap with one hand, improving ease of use. Then, you build a drop-down menu around that choice — placing the most popular or needed options at the bottom instead of the top for easy tapping. From there, you may have to rethink larger-scale layouts to work around those interactive elements being lower on the screen, slowly addressing larger categories until you have a finished site design.
In either case, the idea of bottom-up design is to begin with the most specific problems someone might have. You then address them in sequence instead of determining the big picture first.
While neither bottom-up nor top-down approaches dominate the industry, some web engineers prefer the bottom-up approach due to the various accessibility benefits this process provides. This strategy has several accessibility benefits.
The biggest benefit of bottom-up methods is that they prioritize the user’s needs.
Top-down approaches seem organized, but they often result in a site that reflects the designer’s choices and beliefs more than it serves your audience.
“
Consider some of the complaints that social media users have made over the years related to usability and accessibility for the everyday user. For example, many users complain that their Facebook feed will randomly refresh as they scroll for the sake of providing users with the most up-to-date content. However, the feature makes it virtually impossible to get back to a post a user viewed that they didn’t think to save. Likewise, TikTok’s watch history feature has come and gone over the years and still today is difficult for many users to find without viewing an outside tutorial on the subject.
This is a common problem: 95.9% of the largest one million homepages have Web Content Accessibility Guidelines (WCAG) errors. While a bottom-up alternative doesn’t mean you won’t make any mistakes, it may make them less likely, as bottom-up thinking often improves your awareness of new stimuli so you can catch things you’d otherwise overlook. It’s easier to meet user’s needs when you build your entire site around their experience instead of looking at UX as an afterthought.
Consider this example from Berkshire Hathaway, a multi-billion-dollar holding company. The overall design philosophy is understandable: It’s simple and direct, choosing to focus on information instead of fancy aesthetics that may not suit the company image. However, you could argue it loses itself in this big picture.
While it is simple, the lack of menus or color contrast and the small font make it harder to read and a little overwhelming. This confusion can counteract any accessibility benefits of its simplicity.
Alternatively, even a simple website redesign could include intuitive menus, additional contrast, and accessible font for easy navigation across the site.
The homepage for U.K. charity Scope offers a better example of web design centered around users’ needs. Concise, clear menus line the top of the page to aid quicker, easier navigation. The color scheme is simple enough to avoid confusion but provides enough contrast to make everything easy to read — something the sans-serif font further helps.
A top-down method also makes catering to a diverse audience difficult because you may need to shoehorn features into an existing design.
For example, say, a local government agency creates a website focused on providing information and services to a general audience of residents. The site originally featured high-resolution images, bright colors, and interactive charts.
However, they realize the images are not accessible to people navigating the site with screen readers, while multiple layers of submenus are difficult for keyboard-only users. Further, the bright colors make it hard for visually impaired users to read the site’s information.
The agency, realizing these accessibility concerns, adds captions to each image. However, the captions disrupt the originally intended visual aesthetics and user flow. Further, adjusting the bright colors would involve completely rethinking the site’s entire color palette. If these considerations had been made before the site was built, the site build could have specifically accommodated these elements while still creating an aesthetically pleasing and user-friendly result.
Alternatively, a site initially built with high contrast, a calm color scheme, clear typography, simple menus, and reduced imagery would make this site much more accessible to a wide user base from the get-go.
As a real-world example, consider the Awwwards website. There are plenty of menus to condense information and ease navigation without overloading the screen — a solid accessibility choice. However, there does not seem to be consistent thought in these menus’ placement or organization.
There are far too many menus; some are at the top while others are at the bottom, and a scrolling top bar adds further distractions. It seems like Awwwards may have added additional menus as an afterthought to improve navigation. This leads to inconsistencies and crowding because they did not begin with this thought.
In contrast,
Bottom-up alternatives address usability issues from the beginning, which results in a more functional, accessible website.
“
Redesigning a system to address a usability issue it didn’t originally make room for is challenging. It can lead to errors like broken links and other unintended consequences that may hinder access for other visitors. Some sites have even claimed to lose 90% of their traffic after a redesign. While bottom-up approaches won’t eliminate those possibilities, they make them less likely by centering everything around usage from the start.
The website for the Vasa Museum in Stockholm, Sweden, showcases a more cohesive approach to ensuring accessibility. Like Awwwards, it uses menus to aid navigation and organization, but there seems to be more forethought into these features. All menus are at the top, and there are fewer of them, resulting in less clutter and a faster way to find what you’re looking for. The overall design complements this by keeping things simple and neat so that the menus stand out.
Similarly, bottom-up design ensures you don’t miss as many accessibility concerns. When you start at the top, before determining what details fit within it, you may not consider all the factors that influence it. Beginning with the specifics instead makes it easier to spot and address problems you would’ve missed otherwise.
This awareness is particularly important for serving a diverse population. An estimated 16% of the global population — 1.6 billion people — have a significant disability. That means there’s a huge range of varying needs to account for, but most people lack firsthand experience living with these conditions. Consequently, it’s easy to miss things impacting others’ UX. You can overcome that knowledge gap by asking how everyone can use your site first.
As these benefits show, a bottom-up design philosophy can be helpful when building an accessible site. Still, top-down methods can be advantageous at times, too. Which is best depends on your situation.
Top-down approaches are a good way to ensure a consistent brand image, as you start with the overall idea and base future decisions on this concept. It also makes it easier to create a design hierarchy to facilitate decision-making within your team. When anyone has a question, they can turn to whoever is above them or refer to the broader goals. Such organization can also mean faster design processes.
Bottom-up methods, by contrast, are better when accessibility for a diverse audience is your main concern. It may be harder to keep everyone on the same overall design philosophy page, but it usually produces a more functional website. You can catch and solve problems early and pay great attention to detail. However, this can mean longer design cycles, which can incur extra costs.
It may come down to what your team is most comfortable with. People think and work differently, with some preferring a top-down approach while others find bottom-up more natural. Combining the two — starting with a top-down model before tackling updates from a bottom-up perspective — can be beneficial, too.
Should you decide a bottom-up design method is best for your goals, here are some ways you can embrace this philosophy.
One of the most important factors in bottom-up web design is to center everything around your users. As a result, your existing user base — whether from a separate part of your business or another site you run — is the perfect place to start.
Survey customers and web visitors about their experience on your sites and others. Ask what pain points they have and what features they’d appreciate. Any commonalities between responses deserve attention. You can also turn to WCAG standards for inspiration on accessible functionality, but first-hand user feedback should form the core of your mission.
Past sites and business projects can also reveal what specifics you should start with. Look for any accessibility gaps by combing through old customer feedback and update histories and using these sites yourself to find issues. Take note of any barriers or usability concerns to address in your next website.
Remember to document everything you find as you go. Up to 90% of organizations’ data is unstructured, making it difficult to analyze later. Reversing that trend by organizing your findings and recording your accessible design process will streamline future accessibility optimization efforts.
Keep in mind that a bottom-up strategy can be time-consuming. One of the reasons why top-down alternatives are popular is because they’re efficient. You can overcome this gap by splitting tasks between smaller teams. However, these groups must communicate frequently to ensure separate design considerations work as a cohesive whole.
A DevOps approach is helpful here. DevOps has helped 49% of its adopters achieve a faster time to market, and 61% report higher-quality deliverables. It also includes space for both detailed work and team-wide meetings to keep everyone on track. Such benefits ensure you can remain productive in a bottom-up strategy.
You can’t overstate the importance of accessible website design. By the same token, bottom-up philosophies are crucial in modern site-building. A detail-oriented approach makes it easier to serve a more diverse audience along several fronts. Making the most of this opportunity will both extend your reach to new niches and make the web a more equitable place.
The Web Accessibility Initiative’s WCAG standards are a good place to start. While these guidelines don’t necessarily describe how to apply a bottom-up approach, they do outline critical user needs and accessibility concerns your design should consider. The site also offers a free and comprehensive Digital Accessibility Foundations course for designers and developers.
Familiarizing yourself with these standards and best practices will make it easier to understand your audience before you begin designing your site. You can then build a more accessible platform from the ground up.
Additionally, the following are some valuable related reads that can act as inspiration in accessibility-centered and user-centric design.
By employing bottom-up thinking as well as resources like these in your design approach, you can create websites that not only meet current accessibility standards but actively encourage site use among users of all backgrounds and abilities.
Generating Unique Random Numbers In JavaScript Using Sets Generating Unique Random Numbers In JavaScript Using Sets Amejimaobari Ollornwi 2024-08-26T15:00:00+00:00 2025-06-25T15:04:30+00:00 JavaScript comes with a lot of built-in functions that allow you to carry out so many different operations. One of these built-in functions is the […]
Accessibility
2024-08-26T15:00:00+00:00
2025-06-25T15:04:30+00:00
JavaScript comes with a lot of built-in functions that allow you to carry out so many different operations. One of these built-in functions is the Math.random()
method, which generates a random floating-point number that can then be manipulated into integers.
However, if you wish to generate a series of unique random numbers and create more random effects in your code, you will need to come up with a custom solution for yourself because the Math.random()
method on its own cannot do that for you.
In this article, we’re going to be learning how to circumvent this issue and generate a series of unique random numbers using the Set
object in JavaScript, which we can then use to create more randomized effects in our code.
Note: This article assumes that you know how to generate random numbers in JavaScript, as well as how to work with sets and arrays.
One of the ways to generate a unique series of random numbers in JavaScript is by using Set
objects. The reason why we’re making use of sets is because the elements of a set are unique. We can iteratively generate and insert random integers into sets until we get the number of integers we want.
And since sets do not allow duplicate elements, they are going to serve as a filter to remove all of the duplicate numbers that are generated and inserted into them so that we get a set of unique integers.
Here’s how we are going to approach the work:
Set
object.Set
until the Set
is filled with a certain number of them.The following is a quick example of how the code comes together:
function generateRandomNumbers(count, min, max) {
// 1: Create a `Set` object
let uniqueNumbers = new Set();
while (uniqueNumbers.size < count) {
// 2: Generate each random number
uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
}
// 3: Immediately insert them numbers into the Set...
return Array.from(uniqueNumbers);
}
// ...set how many numbers to generate from a given range
console.log(generateRandomNumbers(5, 5, 10));
What the code does is create a new Set
object and then generate and add the random numbers to the set until our desired number of integers has been included in the set. The reason why we’re returning an array is because they are easier to work with.
One thing to note, however, is that the number of integers you want to generate (represented by count
in the code) should be less than the upper limit of your range plus one (represented by max + 1
in the code). Otherwise, the code will run forever. You can add an if statement
to the code to ensure that this is always the case:
function generateRandomNumbers(count, min, max) {
// if statement checks that `count` is less than `max + 1`
if (count > max + 1) {
return "count cannot be greater than the upper limit of range";
} else {
let uniqueNumbers = new Set();
while (uniqueNumbers.size < count) {
uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
}
return Array.from(uniqueNumbers);
}
}
console.log(generateRandomNumbers(5, 5, 10));
It is one thing to generate a series of random numbers. It’s another thing to use them.
Being able to use a series of random numbers with arrays unlocks so many possibilities: you can use them in shuffling playlists in a music app, randomly sampling data for analysis, or, as I did, shuffling the tiles in a memory game.
Let’s take the code from the last example and work off of it to return random letters of the alphabet. First, we’ll construct an array of letters:
const englishAlphabets = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
// rest of code
Then we map
the letters in the range of numbers:
const englishAlphabets = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
// generateRandomNumbers()
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
In the original code, the generateRandomNumbers()
function is logged to the console. This time, we’ll construct a new variable that calls the function so it can be consumed by randomAlphabets
:
const englishAlphabets = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
// generateRandomNumbers()
const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
Now we can log the output to the console like we did before to see the results:
const englishAlphabets = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
// generateRandomNumbers()
const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
console.log(randomAlphabets);
And, when we put the generateRandomNumbers
()
function definition back in, we get the final code:
const englishAlphabets = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
function generateRandomNumbers(count, min, max) {
if (count > max + 1) {
return "count cannot be greater than the upper limit of range";
} else {
let uniqueNumbers = new Set();
while (uniqueNumbers.size < count) {
uniqueNumbers.add(Math.floor(Math.random() * (max - min + 1)) + min);
}
return Array.from(uniqueNumbers);
}
}
const randomIndexes = generateRandomNumbers(5, 0, 25);
const randomAlphabets = randomIndexes.map((index) => englishAlphabets[index]);
console.log(randomAlphabets);
So, in this example, we created a new array of alphabets by randomly selecting some letters in our englishAlphabets
array.
You can pass in a count argument of englishAlphabets.length
to the generateRandomNumbers
function if you desire to shuffle the elements in the englishAlphabets
array instead. This is what I mean:
generateRandomNumbers(englishAlphabets.length, 0, 25);
In this article, we’ve discussed how to create randomization in JavaScript by covering how to generate a series of unique random numbers, how to use these random numbers as indexes for arrays, and also some practical applications of randomization.
The best way to learn anything in software development is by consuming content and reinforcing whatever knowledge you’ve gotten from that content by practicing. So, don’t stop here. Run the examples in this tutorial (if you haven’t done so), play around with them, come up with your own unique solutions, and also don’t forget to share your good work. Ciao!
Regexes Got Good: The History And Future Of Regular Expressions In JavaScript Regexes Got Good: The History And Future Of Regular Expressions In JavaScript Steven Levithan 2024-08-20T15:00:00+00:00 2025-06-25T15:04:30+00:00 Modern JavaScript regular expressions have come a long way compared to what you might be familiar with. […]
Accessibility
2024-08-20T15:00:00+00:00
2025-06-25T15:04:30+00:00
Modern JavaScript regular expressions have come a long way compared to what you might be familiar with. Regexes can be an amazing tool for searching and replacing text, but they have a longstanding reputation (perhaps outdated, as I’ll show) for being difficult to write and understand.
This is especially true in JavaScript-land, where regexes languished for many years, comparatively underpowered compared to their more modern counterparts in PCRE, Perl, .NET, Java, Ruby, C++, and Python. Those days are over.
In this article, I’ll recount the history of improvements to JavaScript regexes (spoiler: ES2018 and ES2024 changed the game), show examples of modern regex features in action, introduce you to a lightweight JavaScript library that makes JavaScript stand alongside or surpass other modern regex flavors, and end with a preview of active proposals that will continue to improve regexes in future versions of JavaScript (with some of them already working in your browser today).
ECMAScript 3, standardized in 1999, introduced Perl-inspired regular expressions to the JavaScript language. Although it got enough things right to make regexes pretty useful (and mostly compatible with other Perl-inspired flavors), there were some big omissions, even then. And while JavaScript waited 10 years for its next standardized version with ES5, other programming languages and regex implementations added useful new features that made their regexes more powerful and readable.
But that was then.
Did you know that nearly every new version of JavaScript has made at least minor improvements to regular expressions?
Let’s take a look at them.
Don’t worry if it’s hard to understand what some of the following features mean — we’ll look more closely at several of the key features afterward.
/[/]/
).y
(sticky
), which made it easier to use regexes in parsers, and u
(unicode
), which added several significant Unicode-related improvements along with strict errors. It also added the RegExp.prototype.flags
getter, support for subclassing RegExp
, and the ability to copy a regex while changing its flags.s
(dotAll
) flag, lookbehind, named capture, and Unicode properties (via p{...}
and P{...}
, which require ES6’s flag u
). All of these are extremely useful features, as we’ll see.matchAll
, which we’ll also see more of shortly.d
(hasIndices
), which provides start and end indices for matched substrings.v
(unicodeSets
) as an upgrade to ES6’s flag u
. The v
flag adds a set of multicharacter “properties of strings” to p{...}
, multicharacter elements within character classes via p{...}
and q{...}
, nested character classes, set subtraction [A--B]
and intersection [A&&B]
, and different escaping rules within character classes. It also fixed case-insensitive matching for Unicode properties within negated sets [^...]
.As for whether you can safely use these features in your code today, the answer is yes! The latest of these features, flag v
, is supported in Node.js 20 and 2023-era browsers. The rest are supported in 2021-era browsers or earlier.
Each edition from ES2019 to ES2023 also added additional Unicode properties that can be used via p{...}
and P{...}
. And to be a completionist, ES2021 added string method replaceAll
— although, when given a regex, the only difference from ES3’s replace
is that it throws if not using flag g
.
With all of these changes, how do JavaScript regular expressions now stack up against other flavors? There are multiple ways to think about this, but here are a few key aspects:
x
(“extended”) flag that allows insignificant whitespace and comments. Additionally, it lacks regex subroutines and subroutine definition groups (from PCRE and Perl), a powerful set of features that enable writing grammatical regexes that build up complex patterns via composition.So, it’s a bit of a mixed bag.
JavaScript regexes have become exceptionally powerful, but they’re still missing key features that could make regexes safer, more readable, and more maintainable (all of which hold some people back from using this power).
“
The good news is that all of these holes can be filled by a JavaScript library, which we’ll see later in this article.
Let’s look at a few of the more useful modern regex features that you might be less familiar with. You should know in advance that this is a moderately advanced guide. If you’re relatively new to regex, here are some excellent tutorials you might want to start with:
Often, you want to do more than just check whether a regex matches — you want to extract substrings from the match and do something with them in your code. Named capturing groups allow you to do this in a way that makes your regexes and code more readable and self-documenting.
The following example matches a record with two date fields and captures the values:
const record = 'Admitted: 2024-01-01nReleased: 2024-01-03';
const re = /^Admitted: (?<admitted>d{4}-d{2}-d{2})nReleased: (?<released>d{4}-d{2}-d{2})$/;
const match = record.match(re);
console.log(match.groups);
/* → {
admitted: '2024-01-01',
released: '2024-01-03'
} */
Don’t worry — although this regex might be challenging to understand, later, we’ll look at a way to make it much more readable. The key things here are that named capturing groups use the syntax (?<name>...)
, and their results are stored on the groups
object of matches.
You can also use named backreferences to rematch whatever a named capturing group matched via k<name>
, and you can use the values within search and replace as follows:
// Change 'FirstName LastName' to 'LastName, FirstName'
const name = 'Shaquille Oatmeal';
name.replace(/(?<first>w+) (?<last>w+)/, '$<last>, $<first>');
// → 'Oatmeal, Shaquille'
For advanced regexers who want to use named backreferences within a replacement callback function, the groups
object is provided as the last argument. Here’s a fancy example:
function fahrenheitToCelsius(str) {
const re = /(?<degrees>-?d+(.d+)?)Fb/g;
return str.replace(re, (...args) => {
const groups = args.at(-1);
return Math.round((groups.degrees - 32) * 5/9) + 'C';
});
}
fahrenheitToCelsius('98.6F');
// → '37C'
fahrenheitToCelsius('May 9 high is 40F and low is 21F');
// → 'May 9 high is 4C and low is -6C'
Lookbehind (introduced in ES2018) is the complement to lookahead, which has always been supported by JavaScript regexes. Lookahead and lookbehind are assertions (similar to ^
for the start of a string or b
for word boundaries) that don’t consume any characters as part of the match. Lookbehinds succeed or fail based on whether their subpattern can be found immediately before the current match position.
For example, the following regex uses a lookbehind (?<=...)
to match the word “cat” (only the word “cat”) if it’s preceded by “fat ”:
const re = /(?<=fat )cat/g;
'cat, fat cat, brat cat'.replace(re, 'pigeon');
// → 'cat, fat pigeon, brat cat'
You can also use negative lookbehind — written as (?<!...)
— to invert the assertion. That would make the regex match any instance of “cat” that’s not preceded by “fat ”.
const re = /(?<!fat )cat/g;
'cat, fat cat, brat cat'.replace(re, 'pigeon');
// → 'pigeon, fat cat, brat pigeon'
JavaScript’s implementation of lookbehind is one of the very best (matched only by .NET). Whereas other regex flavors have inconsistent and complex rules for when and whether they allow variable-length patterns inside lookbehind, JavaScript allows you to look behind for any subpattern.
matchAll
MethodJavaScript’s String.prototype.matchAll
was added in ES2020 and makes it easier to operate on regex matches in a loop when you need extended match details. Although other solutions were possible before, matchAll
is often easier, and it avoids gotchas, such as the need to guard against infinite loops when looping over the results of regexes that might return zero-length matches.
Since matchAll
returns an iterator (rather than an array), it’s easy to use it in a for...of
loop.
const re = /(?<char1>w)(?<char2>w)/g;
for (const match of str.matchAll(re)) {
const {char1, char2} = match.groups;
// Print each complete match and matched subpatterns
console.log(`Matched "${match[0]}" with "${char1}" and "${char2}"`);
}
Note: matchAll
requires its regexes to use flag g
(global
). Also, as with other iterators, you can get all of its results as an array using Array.from
or array spreading.
const matches = [...str.matchAll(/./g)];
Unicode properties (added in ES2018) give you powerful control over multilingual text, using the syntax p{...}
and its negated version P{...}
. There are hundreds of different properties you can match, which cover a wide variety of Unicode categories, scripts, script extensions, and binary properties.
Note: For more details, check out the documentation on MDN.
Unicode properties require using the flag u
(unicode
) or v
(unicodeSets
).
v
Flag v
(unicodeSets
) was added in ES2024 and is an upgrade to flag u
— you can’t use both at the same time. It’s a best practice to always use one of these flags to avoid silently introducing bugs via the default Unicode-unaware mode. The decision on which to use is fairly straightforward. If you’re okay with only supporting environments with flag v
(Node.js 20 and 2023-era browsers), then use flag v
; otherwise, use flag u
.
Flag v
adds support for several new regex features, with the coolest probably being set subtraction and intersection. This allows using A--B
(within character classes) to match strings in A but not in B or using A&&B
to match strings in both A and B. For example:
// Matches all Greek symbols except the letter 'π'
/[p{Script_Extensions=Greek}--π]/v
// Matches only Greek letters
/[p{Script_Extensions=Greek}&&p{Letter}]/v
For more details about flag v
, including its other new features, check out this explainer from the Google Chrome team.
Emoji are 🤩🔥😎👌, but how emoji get encoded in text is complicated. If you’re trying to match them with a regex, it’s important to be aware that a single emoji can be composed of one or many individual Unicode code points. Many people (and libraries!) who roll their own emoji regexes miss this point (or implement it poorly) and end up with bugs.
The following details for the emoji “👩🏻🏫” (Woman Teacher: Light Skin Tone) show just how complicated emoji can be:
// Code unit length
'👩🏻🏫'.length;
// → 7
// Each astral code point (above uFFFF) is divided into high and low surrogates
// Code point length
[...'👩🏻🏫'].length;
// → 4
// These four code points are: u{1F469} u{1F3FB} u{200D} u{1F3EB}
// u{1F469} combined with u{1F3FB} is '👩🏻'
// u{200D} is a Zero-Width Joiner
// u{1F3EB} is '🏫'
// Grapheme cluster length (user-perceived characters)
[...new Intl.Segmenter().segment('👩🏻🏫')].length;
// → 1
Fortunately, JavaScript added an easy way to match any individual, complete emoji via p{RGI_Emoji}
. Since this is a fancy “property of strings” that can match more than one code point at a time, it requires ES2024’s flag v
.
If you want to match emojis in environments without v
support, check out the excellent libraries emoji-regex and emoji-regex-xs.
Despite the improvements to regex features over the years, native JavaScript regexes of sufficient complexity can still be outrageously hard to read and maintain.
Regular Expressions are SO EASY!!!! pic.twitter.com/q4GSpbJRbZ
— Garabato Kid (@garabatokid) July 5, 2019
ES2018’s named capture was a great addition that made regexes more self-documenting, and ES6’s String.raw
tag allows you to avoid escaping all your backslashes when using the RegExp
constructor. But for the most part, that’s it in terms of readability.
However, there’s a lightweight and high-performance JavaScript library named regex
(by yours truly) that makes regexes dramatically more readable. It does this by adding key missing features from Perl-Compatible Regular Expressions (PCRE) and outputting native JavaScript regexes. You can also use it as a Babel plugin, which means that regex
calls are transpiled at build time, so you get a better developer experience without users paying any runtime cost.
PCRE is a popular C library used by PHP for its regex support, and it’s available in countless other programming languages and tools.
Let’s briefly look at some of the ways the regex
library, which provides a template tag named regex
, can help you write complex regexes that are actually understandable and maintainable by mortals. Note that all of the new syntax described below works identically in PCRE.
By default, regex
allows you to freely add whitespace and line comments (starting with #
) to your regexes for readability.
import {regex} from 'regex';
const date = regex`
# Match a date in YYYY-MM-DD format
(?<year> d{4}) - # Year part
(?<month> d{2}) - # Month part
(?<day> d{2}) # Day part
`;
This is equivalent to using PCRE’s xx
flag.
Subroutines are written as g<name>
(where name refers to a named group), and they treat the referenced group as an independent subpattern that they try to match at the current position. This enables subpattern composition and reuse, which improves readability and maintainability.
For example, the following regex matches an IPv4 address such as “192.168.12.123”:
import {regex} from 'regex';
const ipv4 = regex`b
(?<byte> 25[0-5] | 2[0-4]d | 1dd | [1-9]?d)
# Match the remaining 3 dot-separated bytes
(. g<byte>){3}
b`;
You can take this even further by defining subpatterns for use by reference only via subroutine definition groups. Here’s an example that improves the regex for admittance records that we saw earlier in this article:
const record = 'Admitted: 2024-01-01nReleased: 2024-01-03';
const re = regex`
^ Admitted: (?<admitted> g<date>) n
Released: (?<released> g<date>) $
(?(DEFINE)
(?<date> g<year>-g<month>-g<day>)
(?<year> d{4})
(?<month> d{2})
(?<day> d{2})
)
`;
const match = record.match(re);
console.log(match.groups);
/* → {
admitted: '2024-01-01',
released: '2024-01-03'
} */
regex
includes the v
flag by default, so you never forget to turn it on. And in environments without native v
, it automatically switches to flag u
while applying v
’s escaping rules, so your regexes are forward and backward-compatible.
It also implicitly enables the emulated flags x
(insignificant whitespace and comments) and n
(“named capture only” mode) by default, so you don’t have to continually opt into their superior modes. And since it’s a raw string template tag, you don’t have to escape your backslashes like with the
RegExp
constructor.
Atomic groups and possessive quantifiers are another powerful set of features added by the regex
library. Although they’re primarily about performance and resilience against catastrophic backtracking (also known as ReDoS or “regular expression denial of service,” a serious issue where certain regexes can take forever when searching particular, not-quite-matching strings), they can also help with readability by allowing you to write simpler patterns.
Note: You can learn more in the regex
documentation.
There are a variety of active proposals for improving regexes in JavaScript. Below, we’ll look at the three that are well on their way to being included in future editions of the language.
This is a Stage 3 (nearly finalized) proposal. Even better is that, as of recently, it works in all major browsers.
When named capturing was first introduced, it required that all (?<name>...)
captures use unique names. However, there are cases when you have multiple alternate paths through a regex, and it would simplify your code to reuse the same group names in each alternative.
For example:
/(?<year>d{4})-dd|dd-(?<year>d{4})/
This proposal enables exactly this, preventing a “duplicate capture group name” error with this example. Note that names must still be unique within each alternative path.
This is another Stage 3 proposal. It’s already supported in Chrome/Edge 125 and Opera 111, and it’s coming soon for Firefox. No word yet on Safari.
Pattern modifiers use (?ims:...)
, (?-ims:...)
, or (?im-s:...)
to turn the flags i
, m
, and s
on or off for only certain parts of a regex.
For example:
/hello-(?i:world)/
// Matches 'hello-WORLD' but not 'HELLO-WORLD'
RegExp.escape
This proposal recently reached Stage 3 and has been a long time coming. It isn’t yet supported in any major browsers. The proposal does what it says on the tin, providing the function RegExp.escape(str)
, which returns the string with all regex special characters escaped so you can match them literally.
If you need this functionality today, the most widely-used package (with more than 500 million monthly npm downloads) is escape-string-regexp, an ultra-lightweight, single-purpose utility that does minimal escaping. That’s great for most cases, but if you need assurance that your escaped string can safely be used at any arbitrary position within a regex, escape-string-regexp
recommends the regex
library that we’ve already looked at in this article. The regex
library uses interpolation to escape embedded strings in a context-aware way.
So there you have it: the past, present, and future of JavaScript regular expressions.
If you want to journey even deeper into the lands of regex, check out Awesome Regex for a list of the best regex testers, tutorials, libraries, and other resources. And for a fun regex crossword puzzle, try your hand at regexle.
May your parsing be prosperous and your regexes be readable.