创建无障碍React表单的几个要点(附实例)

137 阅读7分钟

上一篇文章介绍了一些帮助开发者创建无障碍应用程序的工具。表单是许多应用程序的一个常见部分,它们必须是无障碍的,以便许多不同的人可以有效地使用它们。这篇文章涵盖了关于创建无障碍React表单的几个要点。

Automated React Accessibility Checks

一个无法访问的表单

这是一个有几个可访问性问题的表单:

export function InaccessibleForm() {
  ...
  return (
    <form className="inaccessible-form" ...>
      <div className="inaccessible-field">
        <label>Name</label>
        <input type="text" ... />
        {errors.name && <div className="error">You must enter your name</div>}
      </div>
      <div className="inaccessible-field">
        <label>User code</label>
        <div className="user-code-field">
          <input type="text" ... />
          <span className="user-code-help">Enter your 4 digit user code</span>
        </div>
        {errors.userCode && (
          <div className="error">You must enter your 4 character user code</div>
        )}
      </div>
      <div className="inaccessible-field">
        <div id="level-label">Level</div>
        <select ...>
          ...
        </select>
        {errors.level && <div className="error">You must enter your level</div>}
      </div>
      <div className="inaccessible-field">
        <label>Notes</label>
        <textarea ... />
        {errors.notes && <div className="error">You must enter some notes</div>}
      </div>
      <div className="inaccessible-field">
        <input type="checkbox" />
        <label>Agree</label>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

当一个字段编辑器获得焦点时,屏幕阅读器不会读出上述表单中的字段标签。这使得盲人用户很难理解在每个字段中应该输入什么。

此外,屏幕阅读器也不会读出出现的验证错误信息。因此,盲人用户不会知道他们在某个字段中输入了错误的内容。

标签和验证错误信息在视觉上也不是很清晰。这使得视力不佳的用户很难理解这个表格。

下面是一张表单的截图。

Inaccessible form 将字段标签与编辑器联系起来

为了让屏幕阅读器在编辑器获得焦点时能够阅读字段标签,标签必须与编辑器相关联。有几种方法可以做到这一点。

典型的方法是通过label htmlFor 道具(在DOM中是for 属性)来引用编辑器id

<label htmlFor="name">Name</label>
<input id="name" type="text" ... />

也可以反过来,用编辑器的aria-labelledby 道具进行关联。当标签不是一个label 元素时,这很有用:

<div id="level-label">Level</div>
<select aria-labelledby="level-label">
  ...
</select>

这种关联也可以通过将编辑器周围的label 元素包裹起来:

<label>
  <input type="checkbox" />
  Agree
</label>

将字段标签与编辑器相关联的另一个好处是,用户可以点击标签来将焦点设置到表单控件上。此外,点击复选框或单选按钮字段上的标签可以切换选中状态。这在小屏幕上很有帮助,特别是针对小的复选框和单选按钮时。

不错!☺️

将一个编辑器与信息文本联系起来

有时,除了标签之外,一个字段还有额外的信息文本。如果该元素与编辑器相关联,当编辑器获得焦点时,屏幕阅读器将读取它。

编辑器上的aria-describedby ,可以用来将它与包含更多信息的元素联系起来:

<label htmlFor="userCode">User code</label>
<div ...>
  <input
    id="userCode"
    type="text"
    aria-describedby="user-code-help"    ...
  />
  <span id="user-code-help" ...>    Enter your 4 digit user code
  </span>
</div>

在上面的例子中,当input 元素获得焦点时,屏幕阅读器将读出 ***"输入你的4位数用户代码"***除了标签之外,还有 "用户代码".

请注意,这种方法比使用placeholder 道具更可取,因为:

  • 屏幕阅读器对aria-describedby 的支持比placeholder 好得多。
  • 占位指示可能在视觉上不适合方框,就像我们的例子。
  • 当用户开始输入时,占位指示就会消失。

为字段添加自动完成功能

在与其他表单有共同数据的字段上添加自动完成功能,可以让用户使用他们之前提交的数值立即填充这些字段。这对普通用户来说是一个很好的节省时间的方法,对有运动障碍的人特别有帮助。

自动完成可以用autoComplete 道具来控制(渲染为autocomplete 属性),并且可以在inputselecttextarea 元素上使用:

<input
  id="name"
  type="text"
  autoComplete="name"  ...
/>

autoComplete 设置为"name" 值,意味着浏览器将提示以前在表单中填写过的全名。

将一个编辑器标记为必填项

示例表格中的所有字段都是必填的,但从视觉上看,没有任何迹象表明是这样的。此外,屏幕阅读器也不会意识到这些字段是必填的。

我们将在标签后面使用一个*****作为视觉上的必填指标:

<label htmlFor="name">
  Name
  <i aria-hidden="true">*</i></label>

我们在星形元素上使用了aria-hidden ,这样屏幕阅读器就不会宣布这个词了 "星".相反,我们希望屏幕阅读器能够宣布 "必填"- 我们在编辑器元素上使用了aria-required="true"

<input 
  ... 
  aria-required="true" />

注意,我们没有使用required ,因为这将导致浏览器验证该字段,而我们想控制验证(在这个例子中借助React Hook Form)。

将一个编辑器标记为无效

使用aria-invalid ,一个字段的编辑器可以被标记为有效或无效。将其设置为"true" ,意味着该字段是无效的,而将其设置为"false" ,意味着它是有效的:

<input
  id="name"
  type="text"
  aria-invalid={errors.name ? "true" : "false"}  {...register("name", { required: true })}
/>

这个例子中的表单使用了React Hook Form,所以如果一个字段处于errors 的状态,它就是无效的。

当设置为aria-invalid="true" ,该字段获得焦点时,屏幕阅读器会说它是无效的。

可访问的内联验证错误信息

要让屏幕阅读器读取验证错误,有几件事要做:

  • 为了让屏幕阅读器在DOM中首次呈现时宣布错误,包含错误信息的元素需要包含role=alert
  • 为了让屏幕阅读器在无效字段编辑器拥有焦点时宣布错误,该编辑器需要与错误信息元素相关联。这种关联是通过引用字段编辑器中的错误信息ID来实现的aria-describedby 道具。

下面是我们表单中的一个示例字段:

<input 
  ... 
  aria-describedby="name-error" />
{errors.name && (
  <div 
    id="name-error"     role="alert"     ... >
    You must enter your name
  </div>
)}

aria-describedby 可以通过列出由空格分隔的所有错误信息元素ID来引用多个错误信息元素。

一个有趣的情况是,当aria-describedby 用于附加信息时,比如我们表单中的用户代码字段。我们可以同时引用附加信息和错误信息元素,用空格隔开。然而,屏幕阅读器会读出这两条信息,这可能是不可取的。在我们的例子中,我们只在出现错误时引用错误信息;否则,我们就引用说明:

<div className="user-code-field">
  <input
    id="userCode"
    ...
    aria-describedby={errors.userCode ? "user-code-error" : "user-code-help"}  />
  <span id="user-code-help" className="user-code-help">    Enter your 4 digit user code
  </span>
</div>
{errors.userCode && (
  <div id="user-code-error" role="alert" className="error">    You must enter your 4 character user code
  </div>
)}

无障碍的文本颜色对比

无障碍性的另一部分是视觉方面。例如,文本需要清晰可见。这意味着文本的颜色需要与它的背景有一定程度的对比

网页可及性网站上的这个页面详细介绍了如何在Chrome DevTools中检查文本颜色对比。这个工具证实了字段标签没有通过该检查。

Inaccessible text 将表格中的文字变得更深一些,标签变得更粗一些,使表格在视觉上更清晰:

form.accessible-form {
  ...
  color: #555555;
}
form .field label {
  ...
  font-weight: 600;
}

Accessible text 可访问的焦点指示器

焦点指示器是一个视觉指示器,显示哪个元素当前拥有焦点。这对键盘用户来说是至关重要的。

许多应用程序删除了焦点指示器,就像我们的无障碍表单的例子:

form .inaccessible-field input:focus {
  outline: none;
}

键盘用户会发现填写我们的无障碍表单是个挑战,因为不容易知道哪个字段有焦点。所以,我们应该避免像上面的例子那样删除焦点指示器。

浏览器对键盘用户的元素轮廓有一个默认的样式。然而,焦点指示器必须是清晰可见的。这意味着与相邻元素有足够高的颜色对比--不幸的是,浏览器默认的焦点指示器可能无法满足这一要求。😞

Sara Soueidan有一篇关于焦点指示器的可访问性要求的好文章

focus-visible 伪类允许我们在元素被聚焦时设置一个可访问的轮廓:

form .field input:focus-visible {
  outline: 2px solid #737373;
  outline-offset: 2px;
}
form.accessible-form button[type="submit"]:focus-visible {
  outline: 2px solid #11683f;
  outline-offset: 2px;
}

focus-visible 伪类比focus 要好,因为它只适用于键盘用户。例如,如果用户用鼠标点击提交按钮,轮廓不会被应用于focus-visible 伪类,但会被应用于focus

这就是了。现在的表单更容易访问了!😊

这篇文章的代码可以在Codesandbox中找到,链接如下:

🏃 玩转代码