A Guide To Accessible Form Validation

When it comes to form validation, we can explore it from two perspectives: usability and accessibility. “What’s the difference between usability and accessibility?” you may ask. Let’s start from there.

Usability

Usability is about improving a given action until it’s as easy and delightful as possible. For example, making the process of fixing an invalid field easier or writing better descriptions so the user can fill the field without facing an error message.

To get a really good grasp of the challenges in this process, I highly recommend you to read the deep-dive “Designing better inline validations UX” from Vitaly. There, you’ll learn about the different approaches to validate a field and what are the caveats and trade-offs of each one.

Accessibility

Choosing the best UX approach is just half of the challenge. The other half is ensuring that any person knows the field is invalid and easily understands how to fix it. That’s what I’ll explore through this guide.

You can look at ‘Accessibility’ and ‘Usability’ as two equally important universes with their own responsibilities. Accessibility is about ensuring anyone can access the content. Usability is about how easy it is to use the website. Once overlapped will take ‘User Experience’ to its best.

With these two concepts clarified, we are now ready to dive into accessible validations.

Accessibility In Forms

Before we get into validation, let me recap the accessibility fundamentals in forms:

  • Navigation
    The form can be navigated using only the keyboard, so people who don’t use a mouse can still fill and submit the form. This is mostly about setting a compliant focus indicator to each form control.
  • Context
    Each form field must have an accessible name (label), so people who use assistive technologies can identify each field. For example, screen readers would read a field name to its user.

Screen Readers In Forms

Similar to browsers, screen readers (SR) behave slightly differently from each other: different shortcuts, different semantic announcements, and different features support. For example, NVDA works better with Firefox, while VoiceOver works best with Safari, and both have slightly different behaviors. However, this shouldn’t stop us from building the common solid foundations that are strongly supported by all.

A while ago, I asked on Twitter how screen reader users navigate forms. Most prefer to Tab or use special shortcuts to quickly jump through the fields but oftentimes can’t do it. The reason is that we, developers, forget to implement those fields with screen readers in mind most of the time.

Currently, many of the field validations can’t be solved with native HTML elements, so we are left with the last resource: ARIA attributes. By using them, Assistive Technologies like screen readers will better describe a given element to the user.

Through the article, I’m using VoiceOver in macOS Catalina for all the scenarios. Each one includes a Copeden demo and a video recording, which hopefully will give you a better idea of how screen readers behave in forms, field descriptions, and errors.

The Field Instructions

Field Description

The field label is the first visual instruction to know what to fill in, followed by a description when needed. The same way sighted users can see the description (assuming a color contrast that meets WCAG 1.4.3 Contrast Minimum), the SR users also need to be aware of it.

To do so, we can connect the description to the input by using the aria-describedby attribute, which accepts an id pointing to the description element. With it, SR will read the description automatically when the user focuses on the field input.

<!-- note: highlight the aria-describedby -->
<label for="address">Your address</label>
<input id="address" type="text" aria-describedby="addressHint"/>
<span id="addressHint">Remember to include the door and apartment.</span>

In this field, it would cause more harm than good to connect the entire description to the aria-describedby. Instead, I prefer to connect a short description that hints to the user about the full description so they can navigate to it on their own.

<input id="days" type="text" aria-describedby="daysHintShort"/>
<div class="field-hint">
  <span id="daysHintShort" hidden>
    <!-- This text is announced automatically when the input is focused
    and ignored when the screen reader users navigate to it. -->
    Below it's explained how these days influence your benefits.
  <span>
  <div>Depending on how many days....</div>
</div>

As this short description is exclusive to assistive technologies only, we need to hide it from sight users. A possibility could be using the .sr-only technique. However, a side-effect is that the screen reader user would bump into it again when moving to the next element, which is redundant. So, instead, let’s use the hidden attribute, which hides the short description from assistive technologies altogether, but still lets us use the node’s contents as the inputs’ description.

<input id="days" type="text" aria-describedby="daysHintShort"/>
<div class="field-hint">
  
<span id="daysHintShort" hidden>
    <!-- This text is announced automatically when the input is focused,
    and ignored when the screen reader users navigates to it. -->
    Below it's explained how these days influence your benefits.
  </span>
  <div>Depending on how many days....</div>
</div>

I find this pattern very useful for fields with long descriptions or even complex validation descriptions. The tip here is to hint to the users about the full instructions, so they won’t be left alone guessing about it.

Visually most people will recognize this pattern. However, people who use SR will get confused. For instance, Voice Over will announce “Address star, edit text.” Some screen readers might completely ignore it, depending on how strict the verbosity settings are.

This is a perfect scenario of an element that, although it’s visually useful, it’s far from ideal for SR users. There are a few ways to address this asterisk pattern. Personally, I prefer to “hide” the asterisk using aria-hidden="true", which tells all assistive technologies to ignore it. That way, Voice Over will just say “Address, edit text.”

<label for="address" class="field-label">
  Address <span class="field-star" aria-hidden="true">*</span>
</label> 

The Semantic Clue

With the visual clue removed from AT, we still need to semantically tell the input is required. To do so, we could add the required attribute to the element. With that, the SR will say, “Address, required, edit text.”

<input id="address" type="text" required />

Besides adding the necessary semantics, the required attribute also modifies the form behavior. On Chrome 107, when the submit fails, it shows a tooltip with a native error message and focuses the required empty field, like the following:

The Flaws In Default Validations

Probably your designer or client will complain about this default validation because it doesn’t match your website aesthetics. Or your users will complain the error is hard to understand or disappears too soon. As currently, it’s impossible to customize the styling and behavior, so we’ll see ourselves forced to avoid the default field validation and implement our own. And just like that, accessibility is compromised again. As web creators, it’s our duty to ensure the custom validation is accessible, so let’s do it.

The first step is to replace required with aria-required, which will keep the input required semantics without modifying its style or behavior. Then, we’ll implement the error message itself in a second.

<!-- note: this would be a diff -->
<input id="address" type="text" required="required" />
<input id="address" type="text" aria-required="true" />

Here’s a table comparing side by side the difference between required and aria-required:

Function required aria-required
Adds semantics Yes Yes
Prevents invalid submit Yes No
Shows custom error message Yes No
Auto-focus invalid field Yes No

Reminder: ARIA attributes never modify an element’s styles or behavior. It only enhances its semantics.

The Error Message

From a usability standpoint, there’s a lot to take into consideration about error messages. In short, the trick is to write a helpful message without technical jargon that states why the field is incorrect and, when possible, to explain how to fix it. For a deep dive, read how to design better error messages by Vitaly and how Wix rewrote all their error messages.

From an accessibility standpoint, we must ensure anyone not only knows that the field is invalid but also what’s the error message. To mark a field as invalid, we use the ARIA attribute aria-invalid="true", which will make the SR announce that the field is invalid when it’s focused. Then, to also announce the error, we use aria-describedby we learned about before, pointing to the error element:

<input
  id="address"
  type="text"
  required="required"
  aria-invalid="true"
  aria-describedby="addressError addressHint"
/>
<span>
  <p id="addressError">Address cannot be empty.</p>
  <p id="addressHint">Remember to include the door and apartment.</p>
</span>

Invalid Field With Description

A good thing about aria-describedby is that it accepts multiple ids, which is very useful for invalid fields with descriptions. We can pass the id of both elements, and the screen reader will announce both when the input is focused, respecting the order of the ids.

<input
  id="address"
  type="text"
  required="required"
  aria-invalid="true"
  aria-describedby="addressError addressHint"
/>
<span>
  <p id="addressError">Address cannot be empty.</p>
  <p id="addressHint">Remember to include the door and apartment.</p>
</span>

Open the Pen Field Validations — aria-invalid by Sandrina Pereira.

The Future Of ARIA Errors And Its Support

An ARIA attribute dedicated to errors already exists — aria-errormessage — but it’s not yet supported by most screen readers. So, for now, you are better off avoiding it and sticking with aria-describedby.

In the meantime, you could check A11Ysupport to know the support of a given ARIA attribute. You can look at this website as the “caniuse” but for screen readers. It contains detailed test cases for almost every attribute that influences HTML semantics. Just pay attention to the date of the test, as some tests might be too old.

Dynamic Content Is Not Announced By Default

Important to note that although aria-describedby supports multiple ids, if you change them (or the elements’ content) dynamically while the input is focused, the SR won’t re-announce its new content automatically. The same happens to the input label. It will only read the new content after you leave the input and focus it again.

In order for us to announce changes in content dynamically, we’ll need to learn about live regions. Let’s explore that in the next section.

Moments Of Validation

The examples shown so far demonstrate ARIA attributes in static fields. But in real fields, we need to apply them dynamically based on user interactions. Forms are one of the scenarios where JavaScript is fundamental to making our fields fully accessible without compromising modern interactive usability.

Regardless of which moment of validation (usability pattern) you use, any of them can be accomplished with accessibility in mind. We’ll explore three common validation patterns:

  • Instant validation
    The field gets validated on every value change.
  • Afterward validation
    The field gets validated on blur.
  • Submit validation
    The field gets validated on the form submit.

Instant Validation

In this pattern, the field gets validated every time the value changes, and we show the error message immediately after.

In the same way, as the error is shown dynamically, we also want the screen reader to announce it right away. To do so, we must turn the error element in a Live Region, by using aria-live="assertive". Without it, the SR won’t announce the error message, unless the user manually navigates to it.

Open the Pen Field Validations - instant validation by Sandrina Pereira.

Some nice to know about this example:

  • While the input is valid, the aria-invalid can be "false" or be completely absent from the DOM. Both ways work fine.
  • The aria-describedby can be dynamically modified to contain one or multiple ids. However, if modified while the input is focused, the screen reader won’t re-announce its new ids — only when the input gets re-focused again.
  • The aria-live attribute holds many gotchas that can cause more harm than good if used incorrectly. Read “Using aria-live” by Ire Aderinokun to better understand how Live Regions behave and when (not) to use it.
  • From a usability perspective, be mindful that this validation pattern can be annoying, the same way it’s annoying when the error shows up too early while we are still typing our answer.

Afterward Validation

In this pattern, the error message is only shown after the user leaves the field (on blur event). Similar to the ‘Instant Validation’, we need to use the aria-live so that the user knows about the error before start filling the next elements.

Usability tip: I personally prefer to show the on-blur error only if the input value changes. Why? Some screen reader users go through all the fields to know how many exist before starting to actually fill them. This can happen with keyboard users too. Even sight users might accidentally click on one of the fields while scrolling down. All these behaviors would trigger the on-blur error too soon when the intent was just to ‘read’ the field, not to fill it. This slightly different pattern avoids that error flow.

Open the Pen Field Validations - afterward validation by Sandrina Pereira.

Submit Validation

In this pattern, the validation happens when the user submits the form, showing the error message afterward. How and when exactly these errors are shown depends on your design preferences. I’ll go through two of the most common approaches:

In Long Forms

In this scenario, I personally like to show an error summary message, usually placed right before the submit button, so that the chances of being visible on the screen are bigger. This error message should be short, for example, “Failed to save because 3 fields are invalid.”

It’s also common to show the inline error messages of all invalid fields, but this time without aria-live so that the screen reader doesn’t announce all the errors, which can be annoying. Some screen readers only announce the first Live Region (error) in the DOM which can also be miss-leading.

Instead, I add the aria-live="assertive" only to the error summary.

Open the Pen Field Validations - on submit - error summary by Sandrina Pereira.

In the demo above, the error summary has two elements:

<p aria-live="assertive" class="sr-only">
  Failed to save because 2 fields are invalid.
</p>
<p aria-hidden="true">
  Failed to save because 2 fields are invalid.
</p>
  1. The semantic error summary contains a static error summary meant to be announced only on submit. So the aria-live is in this element, alongside the .sr-only to hide it visually.
  2. The visual error summary updates every time the number of invalid fields changes. Announcing that message to SR could be annoying, so it’s only meant for visual updates. It has the aria-hidden so that the screen readers users don’t bump into the error summary twice.

Check the screen reader demo below:

In Short Forms

In very short forms, such as logins, you might prefer not to show an error summary in favor of just the inline error messages. If so, there are two common approaches you can take here:

  1. Add an invisible error summary for screen readers by using the .sr-only we learned above.
  2. Or, when there’s just one invalid field, focus that invalid field automatically using HTMLElement.focus(). This helps keyboard users by not having to tab to it manually, and, thanks to aria-describedby, will make screen readers announce the field error immediately too. Note that here you don’t need aria-live to force the error announcement because the field getting focused is enough to trigger it.

Open the Pen Field Validations - on submit - auto-focus by Sandrina Pereira.

Accessibility Comes Before Usability

I must highlight that this is just one approach among others, such as:

  • Error text: It can be just a simple text or include the number of invalid fields or even add an anchor link to each invalid field.
  • Placement: Some sites show the error summary at the top of the form. If you do this, remember to scroll and focus it automatically so that everyone can see/hear it.
  • Focus: Some sites focus on the error summary, while others don’t. Some focus on the first invalid field and don’t show an error summary at all.

Any of these approaches can be considered accessible as long it’s implemented correctly so that anyone can perceive why the form is invalid. We can always argue that one approach is better than the other, but at this point, the benefits would be mostly around usability and no longer exclusively about accessibility.

Nevertheless, the form error summary is an excellent opportunity to gracefully recover from a low moment in a form journey. In an upcoming article, I will break down these form submit patterns in greater detail from both accessibility and usability perspectives.

Testing Field Validations

Automated accessibility tools catch only around 20-25% of A11Y issues; the more interactive your webpage is, the fewer the bugs it catches. For instance, those tools would not have caught any of the demos explored in this article.

You could write unit tests asserting that the ARIA attributes are used in the right place, but even that doesn’t guarantee that the form works as intended for everyone in every browser.

Accessibility is about personal experiences, which means it relies a lot on manual testing, similar to how pixel-perfect animations are better tested manually too. For now, the most effective accessibility testing is a combination of multiple practices such as automated tools, unit tests, manual tests, and user testing.

In the meantime, I challenge you to try out a screen reader by yourself, especially when you build a new custom interactive element from scratch. You’ll discover a new web dimension, and ultimately, it will make you a better web creator.

Things To Keep In Mind For Accessible Fields

Auto Focusing Invalid Inputs

Above, I mentioned one possible pattern of automatically focusing the first invalid field, so the user doesn’t need to manually navigate to it. Depending on the case, this pattern might be useful or not. In doubt, I prefer to keep things simple and not add auto-focus. If not obvious, let the user read the summary error message, understand it and then navigate to the field by themselves.

Wrapping Everything Inside <label>

It might be tempting to wrap everything about a field inside the <label> element. Well, yes, the assistive technologies would then announce everything inside automatically on input focus. But, depending on how ‘extensive’ the input is, it might sound more confusing than helpful:

  • It’s not clear for screen reader users what exactly the label is.
  • If you include interactive elements inside the label, clicking on them might conflict with the automatic input focus behavior.
  • In unit tests (e.g., Testing Library), you can’t target an input by its label.

Overall, keeping the label separate from everything else has more benefits, including having grainier control over how elements are announced and organized.

Disabling The Submit Button

Preventing the user from submitting an invalid form is the most common reason to disable a button. However, the user probably won’t understand why the button is disabled and won’t know how to fix the errors. That’s a big cognitive effort for such a simple task. Whenever possible, avoid disabled buttons. Let people click buttons at any time and show them the error message with instructions. In the last instance, if you really need a disabled button, consider making it an inclusive disabled button, where anyone can understand and interact with it.

Good UX Is Adaptable

Most physical buildings in the world have at least two ways to navigate them: stairs and lifts. Each one has its unique UX with pros and cons suited for different needs. On the web, don’t fall into the trap of forcing the same pattern on all kinds of users and preferences.

Whenever you find an A11Y issue in a given pattern, try to improve it, but also consider looking for an alternative pattern that can be used simultaneously or toggled.

Remember, every person deserves a good experience, but not every experience is good for everyone.

Wrapping Up

Field validations are one of those web concepts without dedicated HTML elements that suit modern web patterns, so we need ARIA to fill in the gaps. In short, this article covered the most common attributes for field validations that make a difference for many people in their web journey:

  • aria-required: To mark a field as required.
  • aria-invalid: To mark the field as invalid.
  • aria-describedby: To connect the description and error message to the input.
  • aria-live: To announce dynamic error messages.

Accessibility is about people, not about ARIA. Besides those attributes, remember to review your colors, icons, field instructions, and, equally important, the validations themselves. Your duty as a web creator is to ensure everyone can enjoy the web, even when a form fails to submit.

Last but not least, I’d like to appreciate the technical review from Ben Mayers, Kitty Giraudel, and Manuel Matuzović. Because sharing is what makes us better. <3

WCAG References

All the practices we explored in this article are covered by WCAG guideline “3.3 Input Assistance”:

The more I learn about web accessibility, the more I realize accessibility goes beyond complying with web standards. Remember, the WCAG are ‘guidelines’ and not ‘rules’ for a reason. They are there to support you, but if you suspect a guideline doesn’t make sense based on your diverse user research, don’t be afraid to question it and think outside the box. Write about it, and ultimately guidelines will evolve too.

Making Disabled Buttons More Inclusive

Let’s talk about disabled buttons. Specifically, let’s get into why we use them and how we can do better than the traditional disabled attribute in HTML (e.g. <button disabled> ) to mark a button as disabled.

There are lots of use cases where a disabled button makes a lot of sense, and we’ll get to those reasons in just a moment. But throughout this article, we’ll be looking at a form that allows us to add a number of tickets to a shopping cart.

This is a good baseline example because there’s a clear situation for disabling the “Add to cart” button: when there are no tickets to add to the cart.

But first, why disabled buttons?

Preventing people from doing an invalid or unavailable action is the most common reason we might reach for a disabled button. In the demo below, we can only add tickets if the number of tickets being added to the cart is greater than zero. Give it a try:

Allow me to skip the code explanation in this demo and focus our attention on what’s important: the “Add to cart” button.

<button type="submit" disabled="disabled">
  Add to cart
</button>

This button is disabled by the disabled attribute. (Note that this is a boolean attribute, which means that it can be written as disabled or disabled="disabled".)

Everything seems fine… so what’s wrong with it?

Well, to be honest, I could end the article right here asking you to not use disabled buttons because they suck, and instead use better patterns. But let’s be realistic: sometimes disabling a button is the solution that makes the most sense.

With that being said, for this demo purpose, we’ll pretend that disabling the “Add to cart” button is the best solution (spoiler alert: it’s not). We can still use it to learn how it works and improve its usability along the way.

Types of interactions

I’d like to clarify what I mean by disabled buttons being usable. You may think, If the button is disabled, it shouldn’t be usable, so… what’s the catch? Bear with me.

On the web, there are multiple ways to interact with a page. Using a mouse is one of the most common, but there are others, like sighted people who use the keyboard to navigate because of a motor impairment.

Try to navigate the demo above using only the Tab key to go forward and Tab + Shift to go backward. You’ll notice how the disabled button is skipped. The focus goes directly from the ticket input to the “dummy terms” link.

Using the Tab key, it changes the focus from the input to the link, skipping the “Add to cart” button.

Let’s pause for a second and recap the reason that lead us to disable the button in the first place versus what we had actually accomplished.

It’s common to associate “interacting” with “clicking” but they are two different concepts. Yes, click is a type of interaction, but it’s only one among others, like hover and focus.

In other words…

All clicks are interactions, but not all interactions are clicks.

Our goal is to prevent the click, but by using disabled, we are preventing not only the click, but also the focus, which means we might be doing as much harm as good. Sure, this behavior might seem harmless, but it causes confusion. People with a cognitive disability may struggle to understand why they are unable to focus the button.

In the following demo, we tweaked the layout a little. If you use a mouse, and hover over the submit button, a tooltip is shown explaining why the button is disabled. That’s great! But if you use only the keyboard, there’s no way of seeing that tooltip because the button cannot be focused with disabled. Ouch!

Using the mouse, the tooltip on the “Add to cart” button is visible on hover. But the tooltip is missing when using the Tab key.

Allow me to once again skip past the code explanation. I highly recommend reading “Inclusive Tooltips” by Haydon Pickering and “Tooltips in the time of WCAG 2.1” by Sarah Higley to fully understand the tooltip pattern.

ARIA to the rescue

The disabled attribute in a <button> is doing more than necessary. This is one of the few cases I know of where a native HTML attribute can do more harm than good. Using an ARIA attribute can do a better job, allowing us to add not only focus on the button, but do so consistently to create an inclusive experience for more people and use cases.

The disabled attribute correlates to aria-disabled="true". Give the following demo a try, again, using only the keyboard. Notice how the button, although marked disabled, is still accessible by focus and triggers the tooltip!

Using the Tab key, the “Add to cart” button is focused and it shows the tooltip.

Cool, huh? Such tiny tweak for a big improvement!

But we’re not done quite yet. The caveat here is that we still need to prevent the click programmatically, using JavaScript.

elForm.addEventListener('submit', function (event) {
  event.preventDefault(); /* prevent native form submit */

  const isDisabled = elButtonSubmit.getAttribute('aria-disabled') === 'true';

  if (isDisabled || isSubmitting) {
    // return early to prevent the ticket from being added
    return;
  }

  isSubmitting = true;
  // ... code to add to cart...
  isSubmitting = false;
})

You might be familiar with this pattern as a way to prevent double clicks from submitting a form twice. If you were using the disabled attribute for that reason, I’d prefer not to do it because that causes the temporary loss of the keyboard focus while the form is submitting.

The difference between disabled and aria-disabled

You might ask: if aria-disabled doesn’t prevent the click by default, what’s the point of using it? To answer that, we need to understand the difference between both attributes:

Feature / Attributedisabledaria-disabled="true"
Prevent click
Prevent hover
Prevent focus
Default CSS styles
Semantics

The only overlap between the two is semantics. Both attributes will announce that the button is indeed disabled, and that’s a good thing.

Contrary to the disabled attribute, aria-disabled is all about semantics. ARIA attributes never change the application behavior or styles by default. Their only purpose is to help assistive technologies (e.g. screen readers) to announce the page content in a more meaningful and robust way.

So far, we’ve talked about two types of people, those who click and those who Tab. Now let’s talk about another type: those with visual impairments (e.g. blindness, low vision) who use screen readers to navigate the web.

People who use screen readers, often prefer to navigate form fields using the Tab key. Now look an how VoiceOver on macOS completely skips the disabled button.

The VoiceOver screen reader skips the “Add to cart” button when using the Tab key.

Once again, this is a very minimal form. In a longer one, looking for a submit button that isn’t there right away can be annoying. Imagine a form where the submit button is hidden and only visible when you completely fill out the form. That’s what some people feel like when the disabled attribute is used.

Fortunately, buttons with disabled are not totally unreachable by screen readers. You can still navigate each element individually, one by one, and eventually you’ll find the button.

The VoiceOver screen reader is able to find and announce the “Add to cart” button.

Although possible, this is an annoying experience. On the other hand, with aria-disabled, the screen reader will focus the button normally and properly announce its status. Note that the announcement is slightly different between screen readers. For example, NVDA and JWAS say “button, unavailable” but VoiceOver says “button, dimmed.”

The VoiceOver screen reader can find the “Add to cart” button using Tab key because of aria-disabled.

I’ve mapped out how both attributes create different user experiences based on the tools we just used:

Tool / Attributedisabledaria-disabled="true"
Mouse or tapPrevents a button click.Requires JS to prevent the click.
TabUnable to focus the button.Able to focus the button.
Screen readerButton is difficult to locate.Button is easily located.

So, the main differences between both attributes are:

  • disabled might skip the button when using the Tab key, leading to confusion.
  • aria-disabled will still focus the button and announce that it exists, but that it isn’t enabled yet; the same way you might perceive it visually.

This is the case where it’s important to acknowledge the subtle difference between accessibility and usability. Accessibility is a measure of someone being able to use something. Usability is a measure of how easy something is to use.

Given that, is disabled accessible? Yes. Does it have a good usability? I don’t think so.

Can we do better?

I wouldn’t feel good with myself if I finished this article without showing you the real inclusive solution for our ticket form example. Whenever possible, don’t use disabled buttons. Let people click it at any time and, if necessary, show an error message as feedback. This approach also solves other problems:

  • Less cognitive friction: Allow people to submit the form at any time. This removes the uncertainty of whether the button is even disabled in the first place.
  • Color contrast: Although a disabled button doesn’t need to meet the WCAG 1.4.3 color contrast, I believe we should guarantee that an element is always properly visible regardless of its state. But that’s something we don’t have to worry about now because the button isn’t disabled anymore.

Final thoughts

The disabled attribute in <button> is a peculiar case where, although highly known by the community, it might not be the best approach to solve a particular problem. Don’t get me wrong because I’m not saying disabled is always bad. There are still some cases where it still makes sense to use it (e.g. pagination).

To be honest, I don’t see the disabled attribute exactly as an accessibility issue. What concerns me is more of a usability issue. By swapping the disabled attribute with aria-disabled, we can make someone’s experience much more enjoyable.

This is yet one more step into my journey on web accessibility. Over the years, I’ve discovered that accessibility is much more than complying with web standards. Dealing with user experiences is tricky and most situations require making trade-offs and compromises in how we approach a solution. There’s no silver bullet for perfect accessibility.

Our duty as web creators is to look for and understand the different solutions that are available. Only then we can make the best possible choice. There’s no sense in pretending the problems don’t exist.

At the end of the day, remember that there’s nothing preventing you from making the web a more inclusive place.

Bonus

Still there? Let me mention two last things about this demo that I think are worthy:

1. Live Regions will announce dynamic content

In the demo, two parts of the content changed dynamically: the form submit button and the success confirmation (“Added [X] tickets!”).

These changes are visually perceived, however, for people with vision impairments using screen readers, that just ain’t the reality. To solve it, we need to turn those messages into Live Regions. Those allow assistive technologies to listen for changes and announce the updated messages when they happen.

There is a .sr-only class in the demo that hides a <span> containing a loading message, but allows it to be announced by screen readers. In this case, aria-live="assertive" is applied to the <span> and it holds a meaningful message after the form is submitting and is in the process of loading. This way, we can announce to the user that the form is working and to be patient as it loads. Additionally, we do the same to the form feedback element.

<button type="submit" aria-disabled="true">
  Add to cart
  <span aria-live="assertive" class="sr-only js-loadingMsg">
     <!-- Use JavaScript to inject the the loading message -->
  </span>
</button>

<p aria-live="assertive" class="formStatus">
  <!-- Use JavaScript to inject the success message -->
</p>

Note that the aria-live attribute must be present in the DOM right from the beginning, even if the element doesn’t hold any message yet, otherwise, Assistive Technologies may not work properly.

Form submit feedback message being announced by the screen reader.

There’s much more to tell you about this little aria-live attribute and the big things it does. There are gotchas as well. For example, if it is applied incorrectly, the attribute can do more harm than good. It’s worth reading “Using aria-live” by Ire Aderinokun and Adrian Roselli’s “Loading Skeletons” to better understand how it works and how to use it.

2. Do not use pointer-events to prevent the click

This is an alternative (and incorrect) implementation that I’ve seen around the web. This uses pointer-events: none; in CSS to prevent the click (without any HTML attribute). Please, do not do this. Here’s an ugly Pen that will hopefully demonstrate why. I repeat, do not do this.

Although that CSS does indeed prevent a mouse click, remember that it won’t prevent focus and keyboard navigation, which can lead to unexpected outcomes or, even worse, bugs.

In other words, using this CSS rule as a strategy to prevent a click, is pointless (get it?). ;)


The post Making Disabled Buttons More Inclusive appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Striking a Balance Between Native and Custom Select Elements

Here’s the plan! We’re going to build a styled select element. Not just the outside, but the inside too. Total styling control. Plus we’re going to make it accessible. We’re not going to try to replicate everything that the browser does by default with a native <select> element. We’re going to literally use a <select> element when any assistive tech is used. But when a mouse is being used, we’ll show the styled version and make it function as a select element.

That’s what I mean by “hybrid” selects: they are both a native <select> and a styled alternate select in one design pattern.

Custom selects (left) are often used in place of native selects (right) for aesthetics and design consistency.

Select, dropdown, navigation, menu… the name matters

While doing the research for this article, I thought about many names that get tossed around when talking about selects, the most common of which are “dropdown” and “menu.” There are two types of naming mistakes we could make: giving the same name to different things, or giving different names to the same thing. A select can suffer from both mistakes.

Before we move ahead, let me try to add clarity around using “dropdown” as a term. Here’s how I define the meaning of dropdown:

Dropdown: An interactive component that consists of a button that shows and hides a list of items, typically on mouse hover, click or tap. The list is not visible by default until the interaction starts. The list usually displays a block of content (i.e. options) on top of other content.

A lot of interfaces can look like a dropdown. But simply calling an element a “dropdown” is like using “fish” to describe an animal. What type of fish it is? A clownfish is not the same as a shark. The same goes for dropdowns.

Like there are different types of fish in the sea, there are different types of components that we might be talking about when we toss the word “dropdown” around:

  • Menu: A list of commands or actions that the user can perform within the page content.
  • Navigation: A list of links used for navigating through a website.
  • Select: A form control (<select>) that displays a list of options for the user to select within a form.

Deciding what type of dropdown we’re talking about can be a foggy task. Here are some examples from around the web that match how I would classify those three different types. This is based on my research and sometimes, when I can’t find a proper answer, intuition based on my experience.

Dropdown-land: Five scenarios where different dropdowns are used across the internet. Read the table below for a detailed description.
Diagram LabelScenarioDropdown Type
1The dropdown expects a selected option to be submitted within a form context (e.g. Select Age)Select
2The dropdown does not need an active option (e.g. A list of actions: copy, paste and cut)Menu
3The selected option influences the content. (e.g. sorting list)Menu or Select (more about it later)
4The dropdown contains links to other pages. (e.g. A “meganav” with websites links)Disclosure Navigation
5The dropdown has content that is not a list. (e.g. a date picker)Something else that should not be called dropdown

Not everyone perceives and interacts with the internet in the same way. Naming user interfaces and defining design patterns is a fundamental process, though one with a lot of room for personal interpretation. All of that variation is what drives the population of dropdown-land. 

There is a dropdown type that is clearly a menu. Its usage is a hot topic in conversations about accessibility. I won’t talk much about it here, but let me just reinforce that the <menu> element is deprecated and no longer recommended. And here’s a detailed explanation about inclusive menus and menus buttons, including why ARIA menu role should not be used for site navigation.

We haven’t even touched on other elements that fall into a rather gray area that makes classifying dropdowns even murkier because of a lack of practical uses cases from the WCAG community.

Uff… that was a lot. Let’s forget about this dropdown-land mess and focus exclusively on the dropdown type that is clearly a <select> element.

Let’s talk about <select>

Styling form controls is an interesting journey. As MDN puts it, there’s the good, the bad, and the ugly. Good is stuff like <form> which is just a block-level element to style. Bad is stuff like checkboxes, which can be done but is somewhat cumbersome. <select> is definitely in ugly terrain.

A lot of articles have been written about it and, even in 2020, it’s still a challenge to create custom selects and some users still prefer the simple native ones

Among developers, the <select> is the most frustrating form control by far, mainly because of its lack of styling support. The UX struggle behind it is so big that we look for other alternatives. Well, I guess the first rule of <select> is similar to ARIA: avoid using it if you can.

I could finish the article right here with “Don’t use <select>, period.” But let’s face reality: a select is still our best solution in a number of circumstances. That might include scenarios where we’re working with a list that contains a lot of options, layouts that are tight on space, or simply a lack of time or budget to design and implement a great custom interactive component from scratch.

Custom <select> requirements

When we make the decision to create a custom select — even if it’s just a “simple” one — these are the requirements we generally have to work with:

  • There is a button that contains the current selected option.
  • Clicking the box toggles the visibility of the options list (also called listbox).
  • Clicking an option in the listbox updates the selected value. The button text changes and the listbox is closed.
  • Clicking outside the component closes the listbox.
  • The trigger contains a small triangle icon pointing downward to indicate there are options.

Something like this:

Some of you may be thinking this works and is good to go. But wait… does it work for everyone?  Not everyone uses a mouse (or touch screen). Plus, a native <select> element comes with more features we get for free and aren’t included in those requirements, such as:

  • The checked option is perceivable for all users regardless of their visual abilities.
  • The component can interact with a keyboard in a predictable way across all browsers (e.g. using arrow keys to navigate, Enter to select, Esc to cancel, etc.).
  • Assistive technologies (e.g. screen readers) announce the element clearly to users, including its role, name and state.
  • The listbox position is adjusted. (i.e. does not get cut off of the screen).
  • The element respects the user’s operating system preferences (e.g high contrast, color scheme, motion, etc.).

This is where the majority of the custom selects fail in some way. Take a look at some of the major UI components libraries. I won’t mention any because the web is ephemeral, but go give it a try. You’ll likely notice that the select component in one framework behaves differently from another. 

Here are additional characteristics to watch for:

  • Is a listbox option immediately activated on focus when navigating with a keyboard?
  • Can you use Enter and/or Space to select an option?
  • Does the Tab key jump go to the next option in the listbox, or jump to the next form control?
  • What happens when you reach the last option in the listbox using arrow keys? Does it simply stay at the last item, does it go back to the first option, or worst of all, does focus move to the next form control? 
  • Is it possible to jump directly to the last item in the listbox using the Page Down key?
  • Is it possible to scroll through the listbox items if there are more than what is currently in view?

This is a small sample of the features included in a native <select> element.

Once we decide to create our own custom select, we are forcing people to use it in a certain way that may not be what they expect.

But it gets worse. Even the native <select> behaves differently across browsers and screen readers. Once we decide to create our own custom select, we are forcing people to use it in a certain way that may not be what they expect. That’s a dangerous decision and it’s in those details where the devil lives.

Building a “hybrid” select

When we build a simple custom select, we are making a trade-off without noticing it. Specifically, we sacrifice functionality to aesthetics. It should be the other way around.

What if we instead deliver a native select by default and replace it with a more aesthetically pleasing one if possible? That’s where the “hybrid” select idea comes into action. It’s “hybrid” because it consists of two selects, showing the appropriate one at the right moment:

  • A native select, visible and accessible by default
  • A custom select, hidden until it’s safe to be interacted with a mouse

Let’s start with markup. First, we’ll add a native <select> with <option> items before the custom selector for this to work. (I’ll explain why in just a bit.)

Any form control must have a descriptive label. We could use <label>, but that would focus the native select when the label is clicked. To prevent that behavior, we’ll use a <span> and connect it to the select using aria-labelledby.

Finally, we need to tell Assistive Technologies to ignore the custom select, using aria-hidden="true". That way, only the native select is announced by them, no matter what.

<span class="selectLabel" id="jobLabel">Main job role</span>
<div class="selectWrapper">
  <select class="selectNative js-selectNative" aria-labelledby="jobLabel">
    <!-- options -->
    <option></option>
  </select>
  <div class="selectCustom js-selectCustom" aria-hidden="true">
     <!-- The beautiful custom select -->
  </div>
</div>

This takes us to styling, where we not only make things look pretty, but where we handle the switch from one select to the other. We need just a few new declarations to make all the magic happen.

First, both native and custom selects must have the same width and height. This ensures people don’t see major differences in the layout when a switch happens.

.selectNative,
.selectCustom {
  position: relative;
  width: 22rem;
  height: 4rem;
}

There are two selects, but only one can dictate the space that holds them. The other needs to be absolutely positioned to take it out of the document flow. Let’s do that to the custom select because it’s the “replacement” that’s used only if it can be. We’ll hide it by default so it can’t be reached by anyone just yet.

.selectCustom {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
}

Here comes the “funny” part. We need to detect if someone is using a device where hover is part of the primary input, like a computer with a mouse. While we typically think of media queries for responsive breakpoints or checking feature support, we can use it to detect hover support too using @media query (hover :hover), which is supported by all major browsers. So, let’s use it to show the custom select only on devices that have hover:

@media (hover: hover) {
  .selectCustom {
    display: block;
  }
}

Great, but what about people who use a keyboard to navigate even in devices that have hover? What we’ll do is hide the custom select when the native select is in focus. We can reach for an adjacent Sibling combinatioron (+). When the native select is in focus, hide the custom select next to it in the DOM order. (This is why the native select should be placed before the custom one.)

@media (hover: hover) {
  .selectNative:focus + .selectCustom {
    display: none;
  }
}

That’s it! The trick to switch between both selects is done! There are other CSS ways to do it, of course, but this works nicely.

Last, we need a sprinkle of JavaScript. Let’s add some event listeners:

  • One for click events that trigger the custom select to open and reveal the options
  • One to sync both selects values. When one select value is changed, the other select value updates as well
  • One for basic keyboard navigation controls, like navigation with Up and Down keys, selecting options with the Enter or Space keys, and closing the select with Esc

Usability testing

I conducted a very small usability test where I asked a few people with disabilities to try the hybrid select component. The following devices and tools were tested using the latest versions of Chrome (81), Firefox (76) and Safari (13):

  • Desktop device using mouse only
  • Desktop device using keyboard only
  • VoiceOver on MacOS using keyboard
  • NVDA on Windows using keyboard
  • VoiceOver on iPhone and iPad using Safari

All these tests worked as expected, but I believe this could have even more usability tests with more diverse people and tools. If you have access to other devices or tools — such as JAWS, Dragon, etc. — please tell me how the test goes.

An issue was found during testing. Specifically, the issue was with the VoiceOver setting “Mouse pointers: Moves Voice Over cursor.” If the user opens the select with a mouse, the custom select will be opened (instead of the native) and the user won’t experience the native select.

What I most like about this approach is how it uses the best of both worlds without compromising the core functionality:

  • Users on mobile and tablets get the native select, which generally offers a better user experience than a custom select, including performance benefits.
  • Keyboard users get to interact with the native select the way they would expect.
  • Assistive Technologies can interact with the native select like normal.
  • Mouse users get to interact with the enhanced custom select.

This approach provides essential native functionality for everyone without the extra huge code effort to implement all the native features.

Don’t get me wrong. This technique is not a one-size-fits-all solution. It may work for simple selects but probably won’t work for cases that involve complex interactions. In those cases, we’d need to use ARIA and JavaScript to complement the gaps and create a truly accessible custom select.

A note about selects that look like menus

Let’s take a look back at the third Dropdown-land scenario. If you recall, it’s  a dropdown that always has a checked option (e.g. sorting some content). I classified it in the gray area, as either a menu or a select. 

Here’s my line of thought: Years ago, this type of dropdown was implemented mostly using a native <select>. Nowadays, it is common to see it implemented from scratch with custom styles (accessible or not). What we end up with is a select element that looks like a menu. 

Three similar dropdowns that always have a selected option.

A <select>  is a type of menu. Both have similar semantics and behavior, especially in a scenario that involves a list of options where one is always checked.  Now, let me mention the WCAG 3.2.2 On Input (Level A) criterion:

Changing the setting of any user interface component should not automatically cause a change of context unless the user has been advised of the behavior before using the component.

Let’s put this in practice. Imagine a sortable list of students. Visually, it may be obvious that sorting is immediate, but that’s not necessarily true for everyone. So, when using <select>, we risk failing the WCAG guideline because the page content changed, and ignificantly re-arranging the content of a page is considered a change of context.

To ensure the criterion success, we must warn the user about the action before they interact with the element, or include a <button> immediately after the select to confirm the change.

<label for="sortStudents">
  Sort students
  <!-- Warn the user about the change when a confirmation button is not present. -->
  <span class="visually-hidden">(Immediate effect upon selection)</span>
</label>
<select id="sortStudents"> ... </select>

That said, using a <select> or building a custom menu are both good approaches when it comes to simple menus that change the page content. Just remember that your decision will dictate the amount of work required to make the component fully accessible. This is a scenario where the hybrid select approach could be used.

Final words

This whole idea started as an innocent CSS trick but, after all of this research, I was reminded once more that creating unique experiences without compromising accessibility is not an easy task.

Building truly accessible select components (or any kind of dropdown) is harder than it looks. WCAG provides excellent guidance and best practices, but without specific examples and diverse practical uses cases, the guidelines are mostly aspirational. That’s not to mention the fact that ARIA support is tepid and that native <select> elements look and behave differently across browsers.

The “hybrid” select is just another attempt to create a good looking select while getting as many native features as possible. Don’t look at this technique experiment as an excuse to downplay accessibility, but rather as an attempt to serve both worlds. If you have the resources, time and the needed skills, please do it right and make sure to test it with different users before shipping your component to the world.

P.S. Remember to use a proper name when making a “dropdown” component. 😉

The post Striking a Balance Between Native and Custom Select Elements appeared first on CSS-Tricks.

How Frontend Developers Can Empower Designer’s Work

How Frontend Developers Can Empower Designer’s Work

How Frontend Developers Can Empower Designer’s Work

Sandrina Pereira

This article is mostly directed at you, dear Frontend Developer, who enjoys implementing user interfaces but struggles in aligning expectations with designers you work with. Perhaps you are referred to as the “UI Developer” or “UX Engineer.” Regardless of the title that you carry around, your job (and as well as mine) consists of more than breathing life into design files. We are also responsible for filling the gap between the design and development workflows. However, when crossing that bridge, we are faced with multiple challenges.

Today, I’d like to share with you practical tips that have helped me to collaborate more efficiently with designers in the past years.

I believe it’s our job, as UI Developers, to not only help designers in their journey to learn how the web works, but also to get to know their reality and learn their language.

Understanding UX Designers’ Background

Most of the UX designers (also referred to as Web Designers or Product Designers) out there took their first steps in the design world through tools like Photoshop and Illustrator. Perhaps they were Graphic Designers: their main goal was to create logos and brand identities and to design layouts for magazines. They could also have been Marketing Designers: printing billboards, designing banners and creating infographics.

This means that most UX designers spent their early days designing for print, which is a totally different paradigm from their current medium, the screen. That was their first big challenge. When dealing with print, designers cared about pixel alignment, but on a fixed area (paper). They didn’t have to contend with a dynamic layout (screens). Thinking about text breaking or states of interactions was simply not part of their job either. Designers also had complete freedom over colors, images, and typography without performance constraints.

Fortunately, there have been many efforts from the self-taught UX designers community, to teach development fundamentals, discuss whether designers should learn to code and understand how to best perform hand-off to developers. The same held true for the development side as well (more about that in a minute.) However, there is still friction happening between the two fields.

On the one hand, designers complaining each time an implementation doesn’t match their designs or feeling misunderstood when those are rejected by the developers without a clear explanation. On the other hand, developers might take for granted that designers know something technical when that might not be true. I believe we can all do better than that.

Embracing A New Way Of Thinking

The websites and apps that we are building will be displayed on a wide range of screen sizes. Each person will use them on multiple devices. Our common goal is to create a familiar experience across their journeys.

When we, as developers, think that designers are being picky about pixel alignments, we need to understand why that is. We need to recognize that it’s beyond fidelity and consistency. It’s about the sum of all the parts working together. It’s cohesion. We have to embrace it and do our best to accomplish it. Learning the fundamentals of design principles is a good starting point. Understand the importance of selecting the right colors, how the hierarchy works, and why white space matters.

Note: There’s an online course known as “Design for Developers” and a book called “Refactoring UI” — both are great to get you started. With these, you’ll be able to implement user interfaces with a sharp eye for detail and gain significant leverage when communicating with designers.

Magnifying Your Designers’ Awareness

You might take for granted that designers know the web as much as you do. Well, they might not. Actually, they don’t have to! It’s our responsibility, as developers, to keep them updated with the current state of the web.

I’ve worked with the two sides of this spectrum: Designers who think that anything can be built (such as complex filters, new scroll behaviors or custom form inputs) without giving browser compatibility a thought. Then, there are designers with assumptions about the “low limitations of the web” and just assume by themselves that something is impossible to implement. We need to show them the true possibilities of web design and not let the limitations hold back their skills.

Teach Them Code, Not How To Code

This seems contradictory but bear with me: knowing how to code is at the core of a developer’s job. We work in a fast-paced industry with new stuff popping up every day. It would be a hypocritical request from us to demand designers to learn how to code. However, we can help them to understand code.

Sit next to the designer you work with for 15 minutes and teach them how they can see for themselves whether the specs of an element are correct and how to change them. I find Firefox Page Inspector remarkably user-friendly for this.

Nowadays, it’s a joy to visualize layouts, debug CSS animations and tweak typography. Show them a ready-to-use code playground like Codepen for them to explore. They don’t need to know all CSS specs to understand how the layout paradigm works. However, they need to know how elements are created and behave in order to empower their daily work.

Include Designers In The Development Process

Invite designers to join you in the stand-up meeting, to be part of the QA process and to sit down with you while you refine visual details in your implementations. This will make them understand the constraints of the web and, soon enough, they’ll recognize why a feature takes time to implement.

Ask Designers To Include You In Their Design Process

You’ll realize that they do much more than “make things pretty”. You’ll learn more about the research process and how user testing is done. You’ll discover that for each design proposal they show to you, several other studies were previously dropped. When designers request a change, ask the reason behind it, so you can learn more about the decisions being made. Ultimately, you’ll start to understand why they are picky about white space and alignments, and hopefully, soon you’ll be too!

I find it crucial to treat frontend development as a core part of the design process, and design as a core part of the development process. Advocating a mindset where everyone gets the chance to be involved in the design and development cycle will help us all to better understand each other’s challenges and to create great experiences as well.

We May Use Different Tools, But We Must Speak The Same Language

Now that we are starting to understand each other’s workflow a little better, it’s time for the next step: to speak the same language.

Looking Beyond The Code Editor

Once you start to pay attention to your surroundings, you’ll be better equipped to tackle problems. Get to know the business better and be willing to listen to what designers have to say. That will make you a team member with richer contributions to the project. Collaborating in areas beyond our expertise is the key to creating meaningful conversations and mutual trust.

Using UI Systems As A Contract

Ask designers to share their design system with you (and if they don’t use one, it’s never too late to start). That will save you the bother of handpicking the colors used, figuring out margins or guessing a text style. Make sure you are familiar with the UI System as much as they are.

You might already be familiar with the component-based concept. However, some designers might not perceive it in the same way as you do. Show them how components can help you to build user interfaces more efficiently. Spread that mindset and also say bye-bye to similar-but-not-equal-names: header vs hero, pricing info vs price selector. When a piece of the user interface (a.k.a ‘a component’) is built, stride to use the same names so they can be reflected in both design and code files. Then, when someone says, “We need to tweak the proposal invitation widget,” everyone knows exactly what is being talked about.

Acknowledging The Real Source Of Truth

Despite the fact that the user interface is first created in the design files, the real source of truth is on the development side. At the end of the day, it is what people actually see, right? When a design is updated, it’s a good idea to leave a side note about its development status, especially in projects where a large number of people are involved. This trick helps to keep the communication smooth, so nobody (including you) wonders: “This is different from the live version. Is it a bug or hasn’t so-and-so been implemented just yet?

Keeping The Debt Under Control

So, you know all about technical debt — it happens when the cost to implement something new is high because of a shortcut we took in the past to meet a deadline. The same can happen on the design side too and we call it design debt.

You can think about it like this: The higher the UX & UI inconsistency, the higher the debt (technical and design). One of the most common inconsistencies is in having different elements to represent the same action. This is why bad design is usually reflected in bad code. It’s up to all of us, both as designers and developers, to treat our design debt seriously.

Being Accessible Doesn’t Mean It Has To Be Ugly

I’m really pleased to see that both we, as developers and designers, are finally starting to bring accessibility into our work. However, a lot of us still think that designing accessible products is hard or limits our skills and creativity.

Let me remind you that we are not creating a product just for ourselves. We are creating a product to be used by all the people, including those who use the product in different ways from you. Take into account how individual elements can be more accessible while keeping the current userflows clear and coherent.

For example, if a designer really believes that creating an enhanced select input is necessary, stand by their side and work together to design and implement it in an accessible way to be used by a wide range of people with diverse abilities.

Giving Valuable Feedback To Designers

It’s unavoidable that questions will pop up when going through the designs. However, that’s not a reason for you to start complaining about the designers’ mistakes or about their ambitious ideas. The designers are not there to drive you mental, they just don’t always intuitively know what you need to do your job. The truth is that, in the past, you didn’t know about this stuff either, so be patient and embrace collaboration as a way of learning.

The Earlier The Feedback, The Better

The timing for feedback is crucial. The feedback workflow depends a lot on the project structure, so there isn’t a one-size-fits-all solution for it. However, when possible, I believe we should aim for a workflow of periodic feedback — starting in the early stages. Having this mindset of open collaboration is the way to reduce the possibility of unexpected big re-iterations later in the road. Keep in mind that the later you give the designer your first feedback, the higher will be the cost for them to explore a new approach if needed.

Explain Abstract Ideas In Simple Terms

Remember when I said that performance was not a concern of the designers’ previous jobs? Don’t freak out when they are not aware of how to create optimized SVGs for the web. When faced with a design that requires a lot of different fonts to be loaded, explain to them why we should minimize the number of requests, perhaps even take advantage of Variable Fonts. Asides from loading faster, it also provides a more consistent user experience. Unless designers are keen to learn, avoid using too many technical terms when explaining something. You can see this as an opportunity of improving your communication skills and clarifying your thoughts.

Don’t Let Assumptions Turn Into Decisions

Some questions about a design spec only show up when we get our hands dirty in the code. To speed things up, we might feel tempted to make decisions based on our assumptions. Please, don’t. Each time you turn an assumption into a decision, you are risking the trust that the design team puts on you to implement the UI. Whenever in doubt, reach out and clarify your doubts. This will show them that you care about the final result as much as they do.

Don’t Do Workarounds By Yourself

When you are asked to implement a design that is too hard, don’t say “It’s impossible” or start to implement a cheap alternative version of it by yourself. This immediately causes friction with the design team when they see their designs were not implemented as expected. They might react angrily, or not say anything but feel defeated or frustrated. That can all be avoided if we explain to them why something it’s not possible, in simple terms and suggest alternative approaches. Avoid dogmatic behaviors and be open to collaborating on a new solution.

Let them know about the Graceful Degradation and Progressive Enhancement techniques and build together an ideal solution and a fallback solution. For example, we can enhance a layout from flexbox to CSS Grid. This allows us to respect the core purpose of a feature and at the same time make the best use of web technologies.

When it comes to animations, let’s be real: deep down you are as thrilled to implement the next wow animation as much as the designers are to create it. The difference between us and them is that we are more aware of the web’s constraints. However, don’t let that limit your own skills! The web evolves fast, so we must use that in our favor.

The next time you are asked to bring a prototype to life, try not to reject it upfront or do it all by yourself. See it as an opportunity to push yourself. Animations are one of the pickiest parts of web development, so make sure to refine each keyframe with your designer — in person or by sharing a private synced link.

Think Beyond The Ideal State — Together

Here’s the thing: it’s not all about you. One of the designers’ challenges is to really understand their users and present the designs in the most attractive way to sell to the Product Manager. Sometimes they forget about what’s beyond the ideal state and developers forget it too.

In order to help avoid these gaps from happening, align with designers the scenario requirements:

  • The worst-case scenario
    A scenario where the worst possibilities are happening. This helps designers to say no to fixed content and let it be fluid. What happens if the title has more than 60 characters or if the list is too long? The same applies to the opposite, the empty scenario. What should the user do when the list is empty?
  • Interaction states
    The browser is more than hovering and clicking around. There are a bunch of states: default, hover, focus, active, disable, error… and some of them can happen at the same time. Let’s give them the proper attention.
  • The loading state
    When building stuff locally, we might use mocks and forget that things actually take time to load. Let designers know about that possibility too and show them that are ways to make people perceive that things don’t take that long to load.

Taking into consideration all these scenarios will save you a lot of time going back and forth during the development phase.

Note: There’s a great article by Scott Hurff about how to fix bad user interfaces that will take us a step closer to an accessible product.

Embrace Change Requests

Developers are known for not being too happy about someone finding a bug in their code — especially when it’s a design bug reported by a designer. There are a lot of memes around it, but have you ever reflected how those bugs can compoundingly rot both the quality of the experience as well as your relationship when these design bugs are casually dismissed? It happens slowly, almost like falling asleep. Bit by bit, then all at once. Designers might start out saying, “It’s just a tiny little detail,” until the indifference and resentment builds up and nothing is said. The damage has then already been done.

This situation is two-fold: both to your peers and to your work. Don’t let that happen. Work on what’s coloring your reaction. A designer being ‘picky’ can be inconvenient, but it can also be an engineer’s shallow interpretation of attentiveness and enthusiasm. When someone tells you that your implementation is not perfect (yet), put your ego aside and show them how you recognize their hard work in refining the final result.

Go Off The Record Once In A While

We all have tasks to deliver and roadmaps to finish. However, some of the best work happens off the record. It won’t be logged into the TO DO board and that’s okay. If you find a designer you have a rapport with, go sneak into their workspace and explore together new experiments. You never know what can come from there!

Conclusion

When you are willing to learn from the design team, you are also learning different ways of thinking. You’ll evolve your mindset of collaboration with other areas out of your experience while refining your eye for creating new experiences. Be there to help and be open to learning, because sharing is what makes us better.



This article wouldn’t be possible without the feedback of many great people. I want to gratefully thank to the designers Jasmine Meijer and Margarida Botelho for helping me to share a balanced perspective about the misunderstandings between designers and developers.

Related Reading on SmashingMag:

Smashing Editorial (ra, yk, il)

Using Custom Properties to Wrangle Variations in Keyframe Animations

Have you ever wondered how to customize CSS animations keyframes without using any preprocessor feature, like mixins? I keep reaching for preprocessors for this reason, but it would be so nice to drop yet one more dependency and go with vanilla CSS.

Well, I found a way to account for variations within a keyframe animation using nothing but CSS and it’s thanks to custom properties! Let’s learn a little more about how CSS keyframes work and how we can enhance them with CSS and a touch of custom properties.

Understanding CSS animations inheritance

When we assign an animation to an element, we can customize some of its properties such as duration, delay, and so on. Let me show you a dummy example: we have two classes: .walk and .run. Both share the same animation (named breath) and .run executes the animation faster than .walk (0.5s to 2s, respectively).

@keyframes breath {
  from {
    transform: scale(0.5);
  }
  to {
    transform: scale(1.5);
  }
}

/* Both share the same animation, but walking is _slower_ than running */
.walk {
  animation: breath 2s alternate;
}

.run {
  animation: breath 0.5s alternate;
}

Each time we reuse an animation, it can behave differently based on the properties we assign it. So, we can say that an animation inherits its behavior based on the element where it’s applied.

But what about the animation *rules* (the scale in this case)? Let’s go back to the breath animation example: The .walk class executes breath slower but it also needs to be deeper, so we need to change its scale value to be larger than it is for the .run class, which takes smaller, more frequent breaths.

The common approach in plain CSS is to duplicate the original animation and tweak the values directly in the class:

@keyframes breath {
  /* same as before... */
}

/* similar to breath, but with different scales */
@keyframes breathDeep {
  from {
    transform: scale(0.3);
  }
  to {
    transform: scale(1.7);
  }
}

.walk {
  animation: breathDeep 2s alternate;
}

.run {
  animation: breath 0.5s alternate;
}

While this works fine, there’s a better solution which allow us to reuse both the animation’s properties and its values! How? By taking advantage of CSS variables inheritance! Let’s see how:

/* breath behaves based on the
CSS variables values that are inherited */
@keyframes breath {
  from {
    transform: scale(var(--scaleStart));
  }
  to {
    transform: scale(var(--scaleEnd));
  }
}

.walk {
  --scaleStart: 0.3;
  --scaleEnd: 1.7;
  animation: breath 2s alternate;
}

.run {
  --scaleStart: 0.8;
  --scaleEnd: 1.2;
  animation: breath 0.5s alternate;
}

Cool, right? Now we don’t need to write duplicate animations to get varying effects from the same animation!

If you need to go even further, remember that we can also update CSS custom properties with JavaScript. Not only root variables, but also in specific elements. I find this incredible powerful because we can create more efficient animations by taking advantage of JavaScript without losing the native optimizations from CSS animation. It’s a win-win!

See the Pen
Dynamic CSS @keyframes w/ CSS Vanilla
by Sandrina Pereira (@sandrina-p)
on CodePen.

The post Using Custom Properties to Wrangle Variations in Keyframe Animations appeared first on CSS-Tricks.

Bringing A Healthy Code Review Mindset To Your Team

Bringing A Healthy Code Review Mindset To Your Team

Bringing A Healthy Code Review Mindset To Your Team

Sandrina Pereira

A ‘code review’ is a moment in the development process in which you (as a developer) and your colleagues work together and look for bugs within a recent piece of code before it gets released. In such a moment, you can be either the code author or one of the reviewers.

When doing a code review, you might not be sure of what you are looking for. On the other side, when submitting a code review, you might not know what to wait for. This lack of empathy and wrong expectations between the two sides can trigger unfortunate situations and rush the process until it ends in an unpleasant experience for both sides.

In this article, I’ll share how this outcome can be changed by changing your mindset during a code review:

👩‍💻👨‍💻 Working As A Team

Foster Out A Culture Of Collaboration

Before we start, it’s fundamental to understand the value of why code needs to be reviewed. Knowledge sharing and team cohesion are beneficial to everyone, however, if done with a poor mindset, a code review can be a huge time consumer with unpleasant outcomes.

The team attitude and behavior should embrace the value of a nonjudgmental collaboration, with the common goal of learning and sharing — regardless of someone else’s experience.

Include Code Reviews In Your Estimations

A complete code review takes time. If a change took one week to be made, don’t expect the code review to take less than a day. It just doesn’t work like that. Don’t try to rush a code review nor look at it as a bottleneck.

Code reviews are as important as writing the actual code. As a team, remember to include code reviews in your workflow and set expectations about how long a code review might take, so everyone is synced and confident about their work.

Save Time With Guidelines And Automatization

An effective way to guarantee consistent contributions is to integrate a Pull Request template in the project. This will help the author to submit a healthy PR with a complete description. A PR description should explain what’s the change purpose, the reason behind it, and how to reproduce it. Screenshots and reference links (Git issue, design file, and so on) are the final touches for a self-explanatory submission.

Doing all of this will prevent early comments from reviewers asking for more details. Another way of making code reviews seem less nitpicky is to use linters to find code formatting and code-quality issues before a reviewer even gets involved. Whenever you see a repetitive comment during the review process, look for ways to minimize it (being with better guidelines or code automatization).

Stay A Student

Anyone can do a code review, and everyone must receive a code review — no matter the seniority level. Receive any feedback gratefully as an opportunity to learn and to share knowledge. Look at any feedback as an open discussion rather than a defensive reaction. As Ryan Holiday says:

“An amateur is defensive. The professional finds learning (and even, occasionally, being shown up) to be enjoyable; they like being challenged and humbled, and engage in education as an ongoing and endless process. (...)”

— Ryan Holiday, Ego Is the Enemy

Stay humble because the second you stop being a student, your knowledge becomes fragile.

The Art Of Selecting Reviewers

In my opinion, picking the reviewers is one of the most important decisions for an effective and healthy code review as a team.

Let’s say your colleague is submitting a code review and decides to tag “everyone” because, unconsciously, she/he might want to speed up the process, deliver the best code possible or making sure everyone knows about the code change. Each one of the reviewers receives a notification, then opens the PR link and sees a lot of people tagged as reviewers. The thought of “someone else will do it” pops up in their minds, leading to ignore the code review and close the link.

Since nobody started the review yet, your colleague will remind each one of the reviewers to do it, i.e. creating pressure on them. Once the reviewers start to do it, the review process takes forever because everyone has their own opinion and it’s a nightmare to reach a common agreement. Meanwhile, whoever decided to not review the code, is then spammed with zillions of e-mail notifications with all of the review comments, thus creating noise in their productivity.

This is something I see happening more than I’d like: Pull Requests with a bunch of people tagged as reviewers and ending, ironically, in a non-productive code review.

There are some common effective flows when selecting the reviewers: A possible flow is to pick two to three colleagues who are familiar with the code and ask them to be reviewers. Another approach, explained by Gitlab team is to have a chained review flow in which the author picks one reviewer who picks another reviewer until at least two reviewers agree with the code. Regardless of the flow you choose, avoid having too many reviewers. Being able to trust your colleagues’ code’s judgment is the key to conduct an effective and healthy code review.

Have Empathy

Spotting pieces of code to improve is just a part of a successful code review. Just as important is to communicate that feedback in a healthy way by showing empathy towards your colleagues.

Before writing a comment, remember to put yourself in the other people’s shoes. It’s easy to be misunderstood when writing, so review your own words before sending them. Even if a conversation starts being ugly, don’t let it affect you — always stay respectful. Speaking well to others is never a bad decision.

Know How To Compromise

When a discussion isn’t solved quickly, take it to a personal call or chat. Analyze together if it’s a subject worth paralyzing the current change request or if it can be addressed in another one.

Be flexible but pragmatic and know how to balance efficiency (delivering) and effectiveness (quality). It’s a compromise to be made as a team. In these moments I like to think of a code review as an iteration rather than a final solution. There’s always room for improvement in the next round.

In-Person Code Reviews

Gathering the author and the reviewer together in a pair programming style can be highly effective. Personally, I prefer this approach when the code review involves complex changes or there’s an opportunity for a large amount of knowledge sharing. Despite this being an offline code review, it’s a good habit to leave online comments about the discussions taken, especially when changes need to be made after the meeting. This is also useful to keep other online reviewers up to date.

Learn From Code Review Outcomes

When a code review has suffered a lot of changes, took too long or has already had too many discussions, gather your team together and analyze the causes and which actions can be taken from it. When the code review is complex, splitting a future task into smaller tasks makes each code review easier.

When the experience gap is big, adopting pair programming is a strategy with incredible results — not only for knowledge sharing but also for off-line collaboration and communication. Whatever the outcome, always aim for a healthy dynamic team with clear expectations.

📝 As An Author

One of the main concerns when working on a code review as an author is to minimize the reviewer’s surprise when reading the code for the first time. That’s the first step to a predictable and smooth code review. Here’s how you can do it.

Establish Early Communication

It’s never a bad idea to talk with your future reviewers before coding too much. Whenever it’s an internal or external contribution, you could do a refinement together or a little bit of pair programming at the beginning of the development to discuss solutions.

There’s nothing wrong in asking for help as a first step. In fact, working together outside the review is a first important step to prevent early mistakes and guarantee an initial agreement. At the same time, your reviewers get aware of a future code review to be made.

Follow The Guidelines

When submitting a Pull Request to be reviewed, remember to add a description and to follow the guidelines. This will save the reviewers from spending time to understand the context of the new code. Even if your reviewers already know what it is about, you can also take this opportunity as a way to improve your writing and communication skills.

Be Your First Reviewer

Seeing your own code in a different context allows you to find things you would miss in your code editor. Do a code review of your own code before asking your colleagues. Have a reviewer mindset and really go through every line of code.

Personally, I like to annotate my own code reviews to better guide my reviewers. The goal here is to prevent comments related to a possible lack of attention and making sure you followed the contribution guidelines. Aim to submit a code review just as you would like to receive one.

Be Patient

After submitting a code review, don’t jump right into a new private message asking your reviewers to “take a look, it only takes a few minutes” and indirectly craving for that thumbs-up emoji. Trying to rush your colleagues to do their work is not a healthy mindset. Instead, trust your colleagues’ workflow as they trust you to submit a good code review. Meanwhile, wait for them to get back to you when they are available. Don’t look at your reviewers as a bottleneck. Remember to be patient because hard things take time.

Be A Listener

Once a code review is submitted, comments will come, questions will be asked, and changes will be proposed. The golden rule here is to not take any feedback as a personal attack. Remember that any code can benefit from an outside perspective.

Don’t look at your reviewers as an enemy. Instead, take this moment to set aside your ego, accept that you make mistakes, and be open to learning from your colleagues, so that you can do a better job the next time.

👀 As A Reviewer

Plan Ahead Of Your Time

When you are asked to be a reviewer, don’t interrupt things right away. That’s a common mistake I see all the time. Reviewing code demands your full attention, and each time you switch code contexts, you are decreasing your productivity by wasting time in recalibrating your focus. Instead, plan ahead by allocating time slots of your day to do code reviews.

Personally, I prefer to do code reviews first thing in the morning or after lunch before picking any other of my tasks. That’s what works best for me because my brain is still fresh without a previous code context to switch off from. Besides that, once I’m done with the review, I can focus on my own tasks, while the author can reevaluate the code based on the feedback.

Be Supportive

When a Pull Request doesn’t follow the contribution guidelines, be supportive — especially to newcomers. Take that moment as an opportunity to guide the author to improve his/her contribution. Meanwhile, try to understand why the author didn’t follow the guidelines in the first place. Perhaps there’s room for improvement that you were not aware of before.

Check Out The Branch And Run It

While reviewing the code, make it run on your own computer — especially when it involves user interfaces. This habit will help you to better understand the new code and spot things you might miss if you just used a default diff-tool in the browser which limits your review scope to the changed code (without having the full context as in your code editor).

Ask Before Assuming

When you don’t understand something, don’t be afraid to say it and ask questions. When asking, remember to first read the surrounding code and avoid making assumptions.

Most of the questions fit in these two types of categories:

  1. “How” Questions
    When you don’t understand how something works or what it does, evaluate with the author if the code is clear enough. Don’t mistake simple code with ignorance. There’s a difference between code that is hard to read and code that you are not aware of. Be open to learn and use a new language feature, even if you don’t know it deeply yet. However, use it only if it simplifies the codebase.
  2. “Why” Questions
    When you don’t understand the “why”, don’t be afraid of suggesting to commenting the code, especially if it’s an edge case or a bug fix. The code should be self-explanatory when it comes to explaining what it does. Comments are a complement to explaining the why behind a certain approach. Explaining the context is highly valuable when it comes to maintainability and it will save someone from deleting a block of code that thought was useless. (Personally, I like to comment on the code until I feel safe about forgetting it later.)

Constructive Criticism

Once you find a piece of code you believe it should be improved, always remember to recognize the author’s effort in contributing to the project and express yourself in a receptive and transparent way.

  • Open discussions.
    Avoid formalizing your feedback as a command or accusation such as “You should…” or “You forgot…”. Express your feedback as an open discussion instead of a mandatory request. Remember to comment on the code, not the author. If the comment is not about the code, then it shouldn’t belong in a code review. As said before, be supportive. Using a passive voice, talking in the plural, expressing questions or suggestions are good approaches to emphasize empathy with the author.
Instead of “Extract this method from here…” prefer “This method should be extracted…” or “What do you think of extracting this method…”
  • Be clear.
    A feedback can easily be misinterpreted, especially when it comes to expressing an opinion versus sharing a fact or a guideline. Always remember to clear that right away on your comment.
Unclear: “This code should be….”

Opinion: “I believe this code should be…”

Fact: “Following [our guidelines], this code should be…”.
  • Explain why.
    What’s obvious for you, might not be for others. It’s never too much explaining the motivation behind your feedback, so the author not only understands how to change something but also what’s the benefit from it.
Opinion: “I believe this code could be…”

Explained: “I believe this code could be (…) because this will improve readability and simplify the unit tests.”
  • Provide examples.
    When sharing a code feature or a pattern which the author is not familiar with, complement your suggestion with a reference as guidance. When possible, go beyond MDN docs and share a snippet or a working example adapted to the current code scenario. When writing an example is too complex, be supportive and offer to help in person or by a video call.
Besides saying something such as “Using filter will help us to [motivation],” also say, “In this case, it could be something like: [snippet of code]. You can check [an example at Finder.js]. Any doubt, feel free to ping me on Slack.”
  • Be reasonable.
    If the same error is made multiple times, prefer to just leave one comment about it and remember the author to review it in the other places, too. Adding redundant comments only creates noise and might be contra-productive.

Keep The Focus

Avoid proposing code changes unrelated to the current code. Before suggesting a change, ask yourself if it’s strictly necessary at that moment. This type of feedback is especially common in refactors. It’s one of the many trade-offs between efficiency and effectiveness that we need to make as a team.

When it comes to refactors, personally, I tend to prefer small but effective improvements. Those are easier to review and there’s less chance of having code conflicts when updating the branch with the target branch.

Set Expectations

If you leave a code review half-done, let the author know about it, so time expectations are under control. In the end, also let the author know if you agree with the new code or if you would like to re-review it once again later.

Before approving a code review, ask yourself if you are comfortable about the possibility of touching that code in the future. If yes, that’s a sign you did a successful code review!

Learn To Refuse A Code Review

Although nobody admits it, sometimes you have to refuse a code review. The moment you decide to accept a code review but try to rush it, the project’s quality is being compromised as well as your team’s mindset.

When you accept to review someone’s else code, that person is trusting your capabilities — it’s a commitment. If you don’t have the time to be a reviewer, just say no to your colleague(s). I really mean it; don’t let your colleagues wait for a code review that will never be done. Remember to communicate and keep expectations clear. Be honest with your team and — even better — with yourself. Whatever your choice, do it healthily, and do it right.

Conclusion

Given enough time and experience, doing code reviews will teach you much more than just technical knowledge. You’ll learn to give and receive feedback from others, as well as make decisions with more thought put into it.

Each code review is an opportunity to grow as a community and individually. Even outside code reviews, remember to foster a healthy mindset until the day it comes naturally to you and your colleagues. Because sharing is what makes us better!

Further Reading on SmashingMag:

Smashing Editorial (ra, yk, il)