Go 中的无障碍:让软件人人可用

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

当代码说:“我能听见你”

\n\n

想象一下,你失明了一天。你最喜欢的基于 Go 的即时通讯软件突然沉默了。按钮不再发声,表单无法填写,验证错误只是红色的方块。感觉不舒服,对吧?然而对数百万残障人士来说,这并非实验,而是现实。

\n\n

无障碍(或 a11y——取自“a”+ 11 个字母 + “y”)不是为了“为了打勾而打勾”。它是为了让你的 Go 服务不为全球 15% 的人口“关灯”。在本文中,我们将探讨如何在不走极端的情况下,将无障碍融入 Go 项目,并带来真正的益处。开始吧!

\n\n

为什么 Go 中的无障碍不是选项,而是标准

\n\n

Go 是一种后端语言,但这并不意味着它不参与创建可访问的界面。如果你为前端编写 API、在服务器端生成 HTML 或创建 CLI 工具——你已经在影响可访问性。坏消息是:许多 Go 开发者认为“a11y 是前端开发者的责任”。好消息是:你可以在一个晚上就改变这一点。

\n\n

从 Go 的角度看,什么是网页可访问性?

\n\n

网页可访问性(web accessibility)是指网站或应用程序能够被有各种障碍(视力、听力、行动能力、认知能力)的人使用的能力。在 Go 的上下文中,这意味着:

\n\n
    \n
  • 由服务器端生成的正确 HTML 标记(例如,通过 html/template);
  • \n
  • 为动态内容设置正确的 ARIA 属性;
  • \n
  • 符合逻辑的键盘导航;
  • \n
  • 清晰的错误消息(不是“Error 500”,而是“无法保存文件:空间不足”)。
  • \n
\n\n

是的,Go 是实现这一点的理想语言。其强类型和内置工具允许你即时检查模板。但让我们循序渐进地来看。

\n\n

语义化标记:Go 中可访问性的基石

\n\n

当你通过 html/template 生成 HTML 时,你就是界面的架构师。如果你的代码在每个按钮上都使用 <div>,屏幕阅读器(screen readers)就无法理解这是什么元素。而如果使用 <button>,它们会说:“按钮。点击以提交表单”。

\n\n

示例:Go 中的可访问表单

\n\n

假设你正在编写一个注册表单。糟糕的版本:

\n\n
// 糟糕:没有语义\ntmpl := `<div class="form">\n    <div>姓名</div>\n    <input type="text" id="name">\n    <div class="error">{{.NameError}}</div>\n</div>`\n
\n\n

好的版本——使用 <label> 和 aria-describedby:

\n\n
// 好:语义化标记 + ARIA\ntmpl := `<form>\n    <label for="name">姓名</label>\n    <input type="text" id="name" aria-describedby="name-error">\n    <span id="name-error" role="alert">{{if .NameError}}{{.NameError}}{{end}}</span>\n</form>`\n
\n\n

发生了什么变化?屏幕阅读器现在知道:这是一个表单,输入字段与标签关联,错误是一个 alert。用户将听到:“姓名。编辑文本。错误:此字段为必填”。

\n\n

ARIA 属性:当 HTML 力不从心时

\n\n

有时标准标签不够用。例如,你在用 Go + HTMX 制作标签页(tabs)。没有 ARIA,它们听起来就像“链接,链接,链接...”。而有了 ARIA,则像“标签页 1,已选中。标签页 2。标签页 3”。

\n\n

如何在 Go 模板中添加 ARIA

\n\n

ARIA 属性作为普通属性传递给 HTML。以下是标签页的示例:

\n\n
<div role="tablist" aria-label="个人资料设置">\n    <button role="tab" aria-selected="{{if eq .Tab "profile"}}true{{else}}false{{end}}" \n            aria-controls="panel-profile" id="tab-profile">个人资料</button>\n    <button role="tab" aria-selected="{{if eq .Tab "security"}}true{{else}}false{{end}}" \n            aria-controls="panel-security" id="tab-security">安全</button>\n</div>\n<div role="tabpanel" id="panel-profile" aria-labelledby="tab-profile">\n    {{template "profile" .}}\n</div>\n<div role="tabpanel" id="panel-security" aria-labelledby="tab-security" hidden>\n    {{template "security" .}}\n</div>`\n
\n\n

现在屏幕阅读器会宣布:“标签列表,个人资料设置。个人资料标签页,已选中。安全标签页”。用户可以使用键盘在标签页之间导航。

\n\n

在 Go 中测试可访问性

\n\n

编写可访问的代码只是成功的一半。你需要测试它。在 Go 生态系统中,你可以使用以下工具:

\n\n
    \n
  • Pa11y — 通过 CI/CD 管道运行,检查 HTML 是否符合 WCAG 标准;
  • \n
  • Axe-core — 可通过 chromedp 或 rod 集成到 Go 测试中;
  • \n
  • WAVE — 用于手动检查的浏览器扩展;
  • \n
  • 屏幕阅读器(NVDA、JAWS、VoiceOver)— 用于真实用户体验测试。
  • \n
\n\n

Go 中的自动化测试示例

\n\n

使用 chromedp,你可以编写检查 ARIA 属性的测试:

\n\n
func TestAccessibleForm(t *testing.T) {\n    ctx, cancel := chromedp.NewContext(context.Background())\n    defer cancel()\n\n    var nameInput string\n    err := chromedp.Run(ctx,\n        chromedp.Navigate("http://localhost:8080/register"),\n        chromedp.AttributeValue("input#name", "aria-describedby", &nameInput, nil),\n    )\n    if err != nil {\n        t.Fatal(err)\n    }\n    if nameInput != "name-error" {\n        t.Errorf("输入字段缺少 aria-describedby 属性")\n    }\n}\n
\n\n

这样的测试可以防止回归,并确保你的 Go 应用程序保持可访问性。

\n\n

结论:可访问性不是功能,而是责任

\n\n

在 Go 中实现可访问性并不困难。它始于语义化标记,通过 ARIA 属性得到增强,并通过测试得到验证。你的用户——无论他们的能力如何——都会感谢你。而你的代码将变得更好:更清晰、更结构化、更易于维护。

\n\n

从今天开始。检查你的模板。添加 label。测试屏幕阅读器。让 Go 为每个人服务。

博客

书籍推荐