资讯动态

Wagtail 无障碍实践全指南:从内容建模到自定义 Axe 内容检查器

发布时间:2026/9/13 22:55:21 来源:尧图企业网站定制
Wagtail 无障碍实践全指南从内容建模到自定义 Axe 内容检查器【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtailCMS 驱动的网站其无障碍水平取决于三件事内容建模是否得当、模板是否可访问、编辑者是否能产出可访问的内容。本文以 Wagtail 的无障碍官方指南docs/advanced_topics/accessibility_considerations.md为主体结合仓库源码系统讲解图片 alt 文本、嵌入标题、标题层级治理、基于 Axe 的内置内容检查器及其自定义扩展帮助你构建一个从编辑端到前端模板都经得起无障碍审计的 Wagtail 站点。Wagtail 将内容建模和前端标记的控制权交给开发者但仍有若干区域需要特别留意同时 Wagtail 也提供了工具帮助编辑者感知可读性与无障碍最佳实践。无障碍网站的建设远不止本文覆盖的内容文末列有延伸学习资源。内容建模阶段的无障碍要点在定义站点模型时以下区域需要格外关注。图片的替代文本Alt Text只要页面中出现图片内容编辑者就应该能够把图片标记为装饰性decorative或提供与上下文相关的替代文本。Wagtail 的富文本编辑器图片嵌入支持这一行为Wagtail 6.3 起新增的ImageBlockwagtail.images.blocks.ImageBlock则为 StreamField 中的图片提供了同样的能力。从源码看ImageBlock本质上是一个带image、alt_text、decorative三个子字段的 StructBlock。在 wagtail/images/blocks.py 中_struct_value_to_image会在图片被标记为装饰性时把alt_text置为空字符串并同步设置contextual_alt_text与decorative属性def _struct_value_to_image(self, struct_value): image struct_value.get(image) decorative struct_value.get(decorative) if image: # If the image is decorative, set alt_text to an empty string image.contextual_alt_text ( if decorative else struct_value.get(alt_text) ) image.decorative decorative return imageImageBlock还内置了与ImageChooserBlock的向后兼容在 wagtail/images/blocks.py 中to_python对旧式的整型图片 ID 或None值会自动构造decorativeFalse、alt_text取image.default_alt_text的结构化值因此你可以直接用ImageBlock替换ImageChooserBlock无需数据迁移详见 docs/reference/streamfield/blocks.md。Wagtail 6.3 同时为内置图片模型以及继承wagtail.images.models.AbstractImage的自定义图片模型新增了可选的description字段。在 wagtail/images/models.py 中该字段被定义为blankTrue, default的CharField而default_alt_text属性的实现则是“description 优先为空时回退到 title”property def default_alt_text(self): # by default the alt text field (used in rich text insertion) is populated # from the description. In the absence of that, it is populated from the title. # Subclasses might provide a separate alt field, and override this return getattr(self, description, None) or self.title这段文本会在富文本插入图片或使用ImageBlock时作为默认 alt 文本提供给编辑者。如果你想定制该行为可以像 docs/advanced_topics/images/custom_image_model.md 展示的那样在自定义图片模型中覆写default_alt_text属性例如强制要求编辑者填写专门写的 alt 文本、而不是回退到常由文件名生成的 titleclass CustomImage(AbstractImage): admin_form_fields ( Image.admin_form_fields ( # 在这里追加你想在表单中展示的字段名例如 caption ) ) property def default_alt_text(self): # Force editors to add specific alt text if description is empty. # Do not use image title which is typically derived from file name. return getattr(self, description, None)设计 alt 文本字段时需注意的重要事项alt 文本应基于图片实际展示的上下文来撰写而非脱离语境的机械描述。alt 文本字段务必保持可选让编辑者能为装饰性图片留空。同一张图片可能在某些场景是装饰性的、在另一些场景不是例如页面列表中的缩略图常常可以视为装饰性。如果 alt 文本的内容已经由页面其他部分承载图片本身不应重复相同的内容。花时间为这些字段提供help_text指导例如链接到成熟的 alt 文本写作资源可在 docs/topics/streamfield.md 中查看更多 StreamField 用法。嵌入内容的标题Embeds Title缺失嵌入标题是无障碍审计中 Wagtail 站点最常见的失败项之一。某些情况下Wagtail 嵌入内容的 iframe 没有设置title属性这常常是 OEmbed 提供商的问题。对屏幕阅读器用户而言这非常麻烦——他们依赖标题来理解嵌入内容是什么、是否值得与之交互。如果你的站点依赖标题缺失的嵌入内容请二选一把 OEmbed 的title字段作为title属性输出到 iframe 上为你的嵌入内容模型增加一个自定义的必填标题字段并将其渲染为 iframe 的title。可用的标题层级Heading LevelsWagtail 让开发者可以非常容易地控制任意内容可用的标题层级——无论是通过富文本功能限制还是自定义 StreamField 块。两种方式下都应限制可用的标题层级让页面的文档大纲document outline更可能保持逻辑与顺序。建议采用以下限制在富文本中禁用h1。每页只应有一个h1通常对应页面的title。页面主体内容只开放h2确有必要时再加h3一般规则是避免其他层级。对于页面特定区块内展示的内容只开放该区块主标题之下的相邻层级。如果通过 StreamField 管理标题务必在那里应用同样的限制。以RichTextField为例传入features即可精确控制可用的标题与格式默认安装可用的标识符包括h2、h3、h4、bold、italic、ol、ul、hr、link、document-link、image、embed另外还有默认未启用的h1、h5、h6、code、superscript、subscript、strikethrough、blockquotebody RichTextField(features[h2, h3, bold, italic, link])富文本中的粗体与斜体默认情况下Wagtail 将粗体存储为b标签、斜体存储为i标签参见 Wagtail 议题 #4665。虽然这些标签的语义不总是完全正确strong与em更通用但对屏幕阅读器用户影响不大——默认情况下屏幕阅读器并不会因为强调样式不同而改变朗读内容。如果你在意这一点可以通过富文本格式转换器format converters更改保存内容时使用的标签。未来富文本重写处理器rewrite handlers还应支持在不改动存储格式的前提下完成这一点见议题 #4223。格式转换器是富文本特性注册表feature registry的核心机制之一register_converter_rule允许register_rich_text_features钩子为某个启用特性定义转换规则从而控制输出 HTML 中实际使用的元素。TableBlock 表格屏幕阅读器会使用行表头与列表头来播报每个表格单元格的上下文。请鼓励编辑者按需设置行表头或列表头。同时始终添加表格标题Caption让屏幕阅读器用户在浏览表格内容之前先对表格内容有一个整体概览。模板中的无障碍以下是让站点模板尽可能无障碍的常见注意事项。模板中的 alt 文本参见上文“内容建模”部分。此外请务必定制图片的 alt 属性——设置为相关字段或者对装饰性图片、alt 文本与其他内容重复的图片设置为空字符串。即使图片模型本身提供了 alt 文本你仍需要针对图片实际使用的上下文决定是否需要 alt 文本。例如在列表页中如果 alt 文本只是重复列表项标题就应避免输出 alt 文本。在 Wagtail 中图片 renditions 自带alt属性上下文 alt 文本或default_alt_text与attrs简写可一次性输出src、width、height、alt四个属性详见 docs/topics/images.mdimg {{ tmp_photo.attrs }} classmy-custom-class /空标题标签Empty Heading Tags在富文本与自定义 StreamField 块中编辑者很容易创建一个标题块却忘记填写内容。内置内容检查器会高亮空标题帮助编辑者发现并修复。如果需要更严格的强制约束为这些字段添加校验规则确保页面无法带着空标题保存例如使用默认即必填的 StreamFieldCharBlockdocs/topics/streamfield.md。考虑为富文本字段添加类似的校验规则。另一种做法是用 CSS 隐藏空标题块h1:empty, h2:empty, h3:empty, h4:empty, h5:empty, h6:empty { display: none; }表单Forms表单构建器Form builder 基于 Django 的表单 API。以下是模板中表单的专属注意事项避免使用as_table、as_ul、as_p这类渲染辅助方法它们会让屏幕阅读器用户更难导航表单或引发 HTML 校验问题。确保必填与选填字段在视觉上可以区分。花时间用fieldset将相关字段分组并配以恰当的legend尤其是单选按钮和复选框。如适用使用合适的autocomplete与autocapitalize属性。对于日期与日期时间字段务必展示预期格式或示例值参考 Django 议题 #32340或者使用input typedate。对于数字字段考虑input typenumber是否真的合适或是否存在更好的替代方案例如inputmode。务必使用辅助技术测试表单实现并参考 W3C 官方的可访问表单开发指南获取更多信息。编写可访问的内容内置工具与扩展Wagtail 提供了一批内置工具与第三方资源帮助创作可访问的内容。内置内容检查器Content CheckerWagtail 在用户工具栏user bar以及支持预览的编辑视图中内置了内容检查器。检查器基于 Axe 测试引擎dequelabs/axe-core扫描已加载页面中的错误帮助编辑者依据 WCAG 等最佳实践与无障碍标准创建更可访问的网站。在源码层面检查器的核心是 wagtail/admin/userbar.py 中的ContentCheckerItem类。其get_axe_configuration会聚合context、options、messages、spec四部分配置由 client/src/includes/userbar.ts 中的initializeAxe读取并调用axe.configure(addCustomChecks(this.axeConfig.spec))注入自定义规则前端自定义检查的求值函数则注册在 client/src/includes/contentChecker.ts 中。默认情况下检查器包含以下规则用于发现创作内容中的常见问题规则 ID作用button-namebutton元素必须始终有文本标签empty-heading检查没有文本内容的标题空标题会迷惑屏幕阅读器用户empty-table-header表格表头文本不应为空frame-titleiframe元素必须始终有文本标签heading-order检查标题顺序标题应按逻辑一致的方式排列主标题h1后跟子标题h2、h3 等input-button-nameinput按钮元素必须始终有文本标签link-namea链接元素必须始终有文本标签p-as-heading检查被样式化成标题的段落其无助于依赖标题导航内容的用户alt-text-quality自定义规则确保图片 alt 文本不包含文件扩展名、下划线等反模式empty-meta-description面向 SEO 的规则确保 meta description 标签存在时有内容这些默认规则定义在ContentCheckerItem的axe_run_only列表、axe_custom_rules与axe_custom_checks中见 wagtail/admin/userbar.py错误提示文案则由axe_messages提供可翻译字符串。其中 Wagtail 自带的alt-text-quality规则前端实现checkImageAltText会用一个正则表达式默认匹配\.(avif|gif|jpg|jpeg|png|svg|webp)$|_检测 alt 文本中的反模式client/src/includes/contentChecker.ts。要自定义检查器的运行方式例如要测试哪些规则可以定义ContentCheckerItem的自定义子类并覆写相应属性然后通过construct_wagtail_userbar钩子把默认实例替换为自定义类的实例。例如Axe 的p-as-heading规则会综合字体粗细、字号与斜体来判定段落是否在视觉上充当标题如果你的标题样式不同可能希望 Axe 只依赖字体粗细来标记短粗体段落from wagtail.admin.userbar import ContentCheckerItem class CustomContentCheckerItem(ContentCheckerItem): def get_axe_custom_checks(self, request): checks super().get_axe_custom_checks(request) # Flag heading-like paragraphs based only on font weight compared to surroundings. checks.append( { id: p-as-heading, options: { margins: [ {weight: 150}, ], passLength: 1, failLength: 0.5, }, }, ) return checks hooks.register(construct_wagtail_userbar) def replace_userbar_content_checker(request, items, page): items[:] [ CustomContentCheckerItem(in_editoritem.in_editor) if isinstance(item, ContentCheckerItem) else item for item in items ]自定义内容检查Custom Content Checks你也可以实现完全自定义的检查用于强制更高级的无障碍检查或其他与无障碍无关的最佳实践。这需要通过钩子配置并使用window.wagtail.userbar.registerCheckAPI 注册任何客户端检查求值函数。首先配置自定义的ContentCheckerItem来添加该检查需要完成四步通过get_axe_custom_checks添加新的 Axe 检查check通过get_axe_custom_rules创建使用该检查的新规则rule通过get_axe_messages为规则提供有用的提示内容配置用户栏条目加载包含该检查的 JS 文件。# wagtail_hooks.py from django.utils.translation import gettext_lazy as _ from wagtail.admin.userbar import ContentCheckerItem class CustomContentCheckerItem(ContentCheckerItem): def get_axe_custom_checks(self, request): checks super().get_axe_custom_checks(request) return checks [ { id: check-element-text, options: {antipattern: ^(click here|click this|go|here|this|start|more|learn more)$}, }, ] def get_axe_custom_rules(self, request): rules super().get_axe_custom_rules(request) return rules [ { id: link-text-quality, impact: serious, selector: a[href], tags: [best-practice], any: [check-element-text], enabled: True, }, ] def get_axe_messages(self, request): messages super().get_axe_messages(request) return { **messages, link-text-quality: { error_name: _(Link does not have descriptive text), help_text: _(Link text should describe the link destination.), }, } class Media: js ( js/custom-checks.js, ) hooks.register(construct_wagtail_userbar) def replace_userbar_content_checker(request, items, page): items[:] [ CustomContentCheckerItem(in_editoritem.in_editor) if isinstance(item, ContentCheckerItem) else item for item in items ]对于自定义检查id是必填且应保持唯一options可选用于向检查函数传递额外参数这里用于配置要标记的链接文本模式。对于自定义规则selector定义了规则会在页面上所有锚元素上检查元素文本规则的any列出它将运行的所有检查。在custom-checks.js中实现求值页面内容的 JavaScript 函数并注册它。registerCheck方法接收两个参数检查标识符与求值函数前端实现见 client/src/includes/userbar.ts底层会把检查注册进 Axe 的配置// static/js/custom-checks.js /** * Checks if the element text matches an antipattern. * param {HTMLElement} node * param {Object} options * param {string} options.antipattern The regex pattern to match against the element text. * returns {boolean} True if the element text does not match the pattern, false otherwise. */ const checkElementText (node, options) { const antipattern new RegExp(options.antipattern, i); return !antipattern.test(node.textContent.trim()); }; window.wagtail.userbar.registerCheck(check-element-text, checkElementText);环境特定检查Environment-Specific Checks生产环境中运行的检查应仅限于内容编辑者自己能够修复的问题对超出其控制范围的事物给出警告只会教会他们忽略所有警告。不过在开发环境中运行额外的检查可能很有用。下面的示例在DEBUG开启时运行全部 WCAG A/AA/AAA 与 best-practice 标签下的 Axe 规则但禁用color-contrast-enhanced生产环境则回退到 Wagtail 默认的创作内容规则from django.conf import settings from wagtail.admin.userbar import ContentCheckerItem class CustomContentCheckerItem(ContentCheckerItem): # Run all Axe rules with these tags in the development environment axe_rules_in_dev [ wcag2a, wcag2aa, wcag2aaa, wcag21a, wcag21aa, wcag22aa, best-practice, ] # Except for the color-contrast-enhanced rule axe_rules { color-contrast-enhanced: {enabled: False}, } def get_axe_run_only(self, request): if settings.DEBUG: return self.axe_rules_in_dev else: # In production, run Wagtails default accessibility rules for authored content only return self.axe_run_only hooks.register(construct_wagtail_userbar) def replace_userbar_content_checker(request, items, page): items[:] [ CustomContentCheckerItem(in_editoritem.in_editor) if isinstance(item, ContentCheckerItem) else item for item in items ]ContentCheckerItem类接受in_editor参数当它在页面编辑器内被实例化时该参数为True。这让你可以根据 Axe 是在页面编辑器还是站点前端运行来定制 Axe 配置。例如当无障碍检查器在无头前端加载时可以修改 Axe spec 中的allowedOrigins属性以允许跨域 iframe 通信from wagtail.admin.utils import get_admin_base_url class HeadlessContentCheckerItem(ContentCheckerItem): def get_axe_spec(self, request): spec super().get_axe_spec(request) spec[allowedOrigins] [ https://my.headless.site # 替换为你的前端 URL if self.in_editor else get_admin_base_url() ] return spec hooks.register(construct_wagtail_userbar) def replace_userbar_content_checker(request, items, page): items[:] [ HeadlessContentCheckerItem(in_editoritem.in_editor) if isinstance(item, ContentCheckerItem) else item for item in items ]ContentCheckerItem 参考以下是ContentCheckerItem类的参考文档属性与对应的可覆写方法一一对应类属性class attributesin_editor是否在页面编辑器中运行内容检查器。axe_includeCSS 选择器列表指定要测试的页面部分默认[body]。axe_excludeCSS 选择器列表从测试中排除的页面部分默认[]用户栏自身总是被默认排除见_axe_default_exclude。axe_run_onlyaxe-core 标签列表或规则 ID 列表二者不可混用设置为假值如None会省略runOnly选项让 Axe 以全部非实验性规则运行。axe_rules规则 ID 到规则选项字典的映射常见格式为{enabled: True/False}可与axe_run_only配合启用或禁用特定规则。axe_custom_rules自定义 Axe 规则列表含 Wagtail 自带的alt-text-quality、empty-meta-description与axe_custom_checks搭配使用请始终设置enabled。axe_custom_checks自定义 Axe 检查列表含 Wagtail 自带的check-image-alt-text、check-empty-meta-description。axe_messages规则 ID 到自定义可翻译字符串的映射作为错误提示若某个已启用规则不在此字典中则回退使用 Axe 自身的错误信息。可覆写方法methods以下方法允许按请求per-request定制上面的属性get_axe_include(request)、get_axe_exclude(request)、get_axe_run_only(request)、get_axe_rules(request)、get_axe_custom_rules(request)、get_axe_custom_checks(request)、get_axe_messages(request)。覆写这些方法时要注意类属性的可变性为避免意外行为应始终返回新对象而不是在方法中直接修改属性。更高级的自定义还可以覆写get_axe_context构造传给axe.run的 context 对象包含include与exclude、get_axe_options构造 options 对象包含runOnly与rules且当runOnly为假值时将其移除以便 Axe 运行全部非实验性规则、get_axe_spec返回包含自定义 rules 与 checks 的 Axe spec。三者最终由get_axe_configuration汇总为一份 JSON 配置由 client/src/includes/contentChecker.ts 中的getAxeConfiguration从页面读取并注入 Axe。第三方工具wagtail-accessibilitywagtail-accessibility是一个第三方包它为 Wagtail 预览添加了 tota11y 无障碍可视化工具包。这让编辑者可以轻松运行基础的无障碍检查——例如校验页面的标题大纲或链接文本。使用 help_text 与 HelpPanel 引导编辑者偶尔使用的编辑者可能不了解站点的内容规范或面向 Web 的写作最佳实践。请利用字段的help_text和HelpPanel提供引导。可读性Readability可读性是无障碍的基础。改善文本内容的方法之一是设定明确的阅读级别/阅读年龄目标可以用wagtail-readinglevel在富文本字段中以分数形式呈现评估结果帮助作者把内容写到目标读者可读的水平。尊重 prefers-reduced-motion有些用户例如患有前庭障碍的用户可能偏好更静态的站点版本。你可以通过 CSS 中的prefers-reduced-motion媒体查询尊重这一偏好media (prefers-reduced-motion) { /* styles to apply if a users device settings are set to reduced motion */ /* for example, disable animations */ * { animation: none !important; transition: none !important; } }注意prefers-reduced-motion只对在操作系统或浏览器中开启了该设置的用户生效Chrome、Safari 与 Firefox 均支持该特性。无障碍延伸学习资源以上内容聚焦 Wagtail 站点特有的无障碍考量但无障碍远不止于此。以下是值得开发者、设计师与作者深入学习的高价值资源W3C 无障碍基础W3C Accessibility FundamentalsThe A11Y ProjectUS GSA – Accessibility for TeamsUK GDS – Dos and donts on designing for accessibilityAccessibility Developer Guide小结Wagtail 的无障碍能力贯穿内容建模、模板渲染与内容创作三个层面用description字段与default_alt_text属性驱动图片替代文本、用ImageBlock覆盖 StreamField 场景、用富文本features限制标题层级、用基于 Axe 的内置内容检查器帮助编辑者自查并通过ContentCheckerItem子类、construct_wagtail_userbar钩子与window.wagtail.userbar.registerCheckAPI 实现按环境定制、规则替换与全新检查。将这些机制组合起来你就能把无障碍从“事后修补”变成内容生产流程中的一环——而这正是构建人人可用的 Wagtail 网站的关键。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价