关于 typo3-accessibility
TYPO3 Accessibility 是面向 TYPO3 v14 网站的技能,用于依据 WCAG 2.2 AA 审查和改进无障碍性。它结合上线检查清单、审计流程与 Fluid 模板模式,并区分自动化证据和仍需人工确认的事项。
主要功能
- 01WCAG 2.2 AA 上线检查清单
- 02axe-core 审计流程
- 03键盘操作与语义结构检查
- 04无障碍 Fluid 模板模式
- 05审计证据及后续事项报告
适用场景
- 01上线前检查无障碍性
- 02审计渲染后的 TYPO3 页面
- 03检查键盘和表单无障碍性
- 04记录审计发现及待确认事项
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dirnbauer/ webconsulting-skills --skill typo3-accessibility安装后在 Claude Code 中输入/typo3-accessibility确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/03-php-accessibility-middleware-and-helpers.md04-javascript-accessible-widgets.md05-css-focus-and-contrast-essentials.md06-typo3-specific-configuration.md07-testing-and-tools.md08-accessibility-extensions.md09-wcag-2-2-quick-reference.md10-anti-patterns-flag-these-in-reviews.mdcredits-and-attribution.mdfull-guide.mdrelated-skills.mdv14-only-accessibility-changes.mdscripts/wcag22_audit.cjs详细介绍
TYPO3 无障碍访问(WCAG 2.2 AA)
**兼容性:**仅适用于 TYPO3 v14.x。本集合不涵盖更早版本的核心。
**TYPO3 API 优先:**始终优先使用 TYPO3 内置 API、Fluid ViewHelpers 和核心功能,然后再添加自定义标记。请确认相关方法在 TYPO3 v14 中存在。
**优先使用 PHP 和 JS,而非 TypoScript:**此技能提供 PHP 中间件、Fluid 局部模板和原生 JavaScript 解决方案。避免使用 TypoScript 示例;请采用基于 PHP 的方案。
1. 无障碍检查清单(上线门槛)
每次部署前都要逐项检查此清单。修复项目后请勾选。
语义结构
- 每个页面恰好有一个
<h1> - 标题层级依次递进(
h1>h2>h3,不得跳级) - 使用地标元素:
<main>、<nav>、<header>、<footer>、<aside> - 跳转到内容的链接是第一个可聚焦元素
-
<html lang="...">与页面语言一致 - 内容中的语言变更使用容器元素上的
lang属性
图像和媒体
- 所有
<img>都有alt属性(装饰性图像使用空的alt="") - 所有
<img>都显式设置width和height(防止 CLS) - 复杂图像(图表、信息图)提供详细描述
- 视频配有字幕
- 音频内容提供文字稿
- 不自动播放带声音的媒体
颜色和对比度
- 文本对比度 >= 4.5:1(普通文本)/ 3:1(大号文本 >= 24px / 18.66px 粗体)
- UI 组件和图形对象的对比度 >= 3:1
- 不单独依靠颜色传达信息(添加图标、文本或图案)
- 深色模式(如已实现)符合对比度要求
键盘和焦点
- 所有交互元素都可通过 Tab 键访问
- 焦点顺序与视觉顺序一致
- 所有交互元素都有可见的焦点指示器(
:focus-visible) - 不得出现焦点陷阱(模态框除外——模态框必须正确限制焦点)
- 按 Escape 键可关闭模态框/覆盖层,并将焦点返回触发元素
- 自定义组件具有正确的键盘事件处理程序
表单
- 每个输入框都有程序化关联的
<label>(显式使用for/id或直接包裹) - 不得仅使用占位符作为标签
- 必填字段有视觉提示,并设置
required属性 - 错误消息通过
aria-describedby+aria-invalid与输入框关联 - 表单分组使用
<fieldset>+<legend> - 输入框设置正确的
type和autocomplete属性
ARIA 和动态内容
- 仅含图标的按钮设置
aria-label - 装饰性图标设置
aria-hidden="true" - 动态更新使用
aria-live="polite"或role="status" - 错误提示使用
role="alert" - 当前导航链接设置
aria-current="page" - 可展开元素设置
aria-expanded
动效和交互
- 遵循
prefers-reduced-motion设置(CSS 和 JS) - 内容每秒闪烁不超过 3 次
- 触控目标符合 WCAG 2.2 AA 2.5.8(最小尺寸为 24×24 CSS 像素);条件允许时采用 44×44 作为更宽松的舒适目标
- 视口 meta 中不得设置
user-scalable=no
TYPO3 特定项
- 后端:文件列表中所有图像均已填写替代文本字段
- 后端:内容元素均有描述性标题(隐藏标题时使用
header_layout = 100) - 后端:页面属性包含合适的
<title>和description - Fluid 模板使用
<f:link.page>/<f:link.typolink>(而非手动设置 href 的<a>) - 内容块 / 内容元素模板遵循下文的无障碍模式
2. WCAG 2.2 一致性审计工作流
收到 WCAG 2.2 审计请求时,应在交付前完成 Agent 能够完成的工作:
- 确定要审计的 URL、语言、视口、表单、对话框、菜单、媒体、下载内容和第三方嵌入内容。
- 对渲染后的页面运行
scripts/wcag22_audit.cjs,并使用 axe-core 标签检查 WCAG 2.0/2.1/2.2 A/AA。 - 检查所有自动化发现的问题和
incomplete项,并修复或记录。审计脚本会尽可能计算color-contrast不完整节点的对比度;升级问题前请先参考这些证据。不要将未解决的incomplete项视为通过。 - 验证语义结构、页面语言、地标、唯一的
<h1>、标题顺序、跳过链接、图像alt、图像尺寸、视口缩放、页脚/页眉导航、可聚焦性、目标尺寸候选项,以及内联文本链接的目标尺寸例外。 - 尽可能通过真实的 Tab 导航检查键盘顺序。立即修复明确的模板/组件缺陷。
- 编写包含两个部分的审计报告:
- Agent 已完成的证据: 命令、URL、视口、工具版本、问题、修复内容、剩余技术风险。
- 仅需人工/用户跟进的事项: 屏幕阅读器确认、法律声明审批,以及仅适用的设备专属、第三方/PDF/媒体或用户测试任务。
收到一致性审计请求时,请遵循 测试与工具 中的详细流程。自动化运行成功并不代表符合 WCAG;它只能为自动化和代码检查所能覆盖的部分提供证据。
3. 流式模板模式
3.1 使用地标的页面布局
<f:render section="Main" />
<f:layout name="Default" />
<f:section name="Main">
<a href="#main-content" class="skip-link">
<f:translate key="LLL:EXT:site_package/Resources/Private/Language/locallang.xlf:skip_to_content" />
</a>
<header>
<f:cObject typoscriptObjectPath="lib.mainNavigation" />
</header>
<main id="main-content">
<f:render section="Content" />
</main>
<footer>
<nav aria-label="{f:translate(key: 'LLL:EXT:site_package/Resources/Private/Language/locallang.xlf:nav.footer')}">
<f:cObject typoscriptObjectPath="lib.footerNavigation" />
</nav>
</footer>
</f:section>
3.2 跳过链接 CSS
.skip-link {
position: absolute;
top: -100%;
left: 0;
z-index: 10000;
padding: 0.75rem 1.5rem;
background: var(--color-primary, #2563eb);
color: var(--color-on-primary, #fff);
font-weight: 600;
text-decoration: none;
}
.skip-link:focus {
top: 0;
}
3.3 无障碍图像渲染
<f:if condition="{image}">
<figure>
<f:image
image="{image}"
alt="{image.alternative}"
title="{image.title}"
width="{dimensions.width}"
height="{dimensions.height}"
loading="{f:if(condition: lazyLoad, then: 'lazy', else: 'eager')}"
additionalAttributes="{decoding: 'async'}"
/>
<f:if condition="{image.description}">
<figcaption>{image.description}</figcaption>
</f:if>
</figure>
</f:if>
对于装饰性图像(不传达信息):
<f:image image="{image}" alt="" />
3.4 无障碍内容元素包装器
<f:if condition="{data.header} && {data.header_layout} != '100'">
<f:switch expression="{data.header_layout}">
<f:case value="1"><h1>{data.header}</h1></f:case>
<f:case value="2"><h2>{data.header}</h2></f:case>
<f:case value="3"><h3>{data.header}</h3></f:case>
<f:case value="4"><h4>{data.header}</h4></f:case>
<f:case value="5"><h5>{data.header}</h5></f:case>
<f:defaultCase><h2>{data.header}</h2></f:defaultCase>
</f:switch>
</f:if>
3.5 无障碍导航局部模板
<nav aria-label="{f:translate(key: 'LLL:EXT:site_package/Resources/Private/Language/locallang.xlf:nav.main')}">
<ul role="list">
<f:for each="{menu}" as="item">
<li>
<f:if condition="{item.active}">
<f:then>
<a href="{item.link}" aria-current="page">{item.title}</a>
</f:then>
<f:else>
<a href="{item.link}">{item.title}</a>
</f:else>
</f:if>
<f:if condition="{item.children}">
<ul>
<f:for each="{item.children}" as="child">
<li>
<a href="{child.link}"
{f:if(condition: child.active, then: 'aria-current="page"')}>
{child.title}
</a>
</li>
</f:for>
</ul>
</f:if>
</li>
</f:for>
</ul>
</nav>
3.6 无障碍折叠面板(内容块 / 自定义 CE)
<div class="accordion" data-accordion>
<f:for each="{items}" as="item" iteration="iter">
<div class="accordion__item">
<div role="heading" aria-level="{f:if(condition: headingLevel, then: headingLevel, else: '3')}">
<button
type="button"
class="accordion__trigger"
aria-expanded="false"
aria-controls="accordion-panel-{data.uid}-{iter.index}"
id="accordion-header-{data.uid}-{iter.index}"
data-accordion-trigger
>
{item.header}
</button>
</div>
<div
id="accordion-panel-{data.uid}-{iter.index}"
role="region"
aria-labelledby="accordion-header-{data.uid}-{iter.index}"
class="accordion__panel"
hidden
>
<f:format.html>{item.bodytext}</f:format.html>
</div>
</div>
</f:for>
</div>
3.7 无障碍标签页组件
<div class="tabs" data-tabs>
<div role="tablist" aria-label="{f:translate(key: 'tabs.label')}">
<f:for each="{items}" as="item" iteration="iter">
<button
type="button"
role="tab"
id="tab-{data.uid}-{iter.index}"
aria-controls="tabpanel-{data.uid}-{iter.index}"
aria-selected="{f:if(condition: iter.isFirst, then: 'true', else: 'false')}"
tabindex="{f:if(condition: iter.isFirst, then: '0', else: '-1')}"
>
{item.header}
</button>
</f:for>
</div>
<f:for each="{items}" as="item" iteration="iter">
<div
role="tabpanel"
id="tabpanel-{data.uid}-{iter.index}"
aria-labelledby="tab-{data.uid}-{iter.index}"
tabindex="0"
{f:if(condition: iter.isFirst, then: '', else: 'hidden')}
>
<f:format.html>{item.bodytext}</f:format.html>
</div>
</f:for>
</div>
详细参考
如果任务需要详细示例、长模板、故障排查矩阵、附录或上文未包含的章节,请阅读完整指南。对于范围较窄的任务,请勿加载此文件,以便该技能遵循渐进式披露。
评论
支持 Markdown