Astro UI 模式与可移植组件库
本指南定义了用于 Astro 站点的标准化 可移植、即插即用 UI 组件 目录。
独立示例采用 可移植组件模型:零外部 npm 依赖,零客户端框架运行时(无 React/Vue),使用带内置回退的命名空间 CSS 变量,并支持渐进增强。在框架项目中,保留语义和交互要求,同时根据所选契约调整组合和样式。在渲染后的项目中验证可访问性;单独的模板无法确保符合 WCAG 标准。
行为契约与框架组合
对于下方索引中的组件,请使用其语义和交互契约:键盘支持、焦点管理、原生元素、可访问名称、状态更新和渐进增强。所选项目/框架契约控制样式、令牌、导入和组件组合。用户指令具有权威性。
示例使用下方的独立可移植组件模型。在添加没有框架组合契约的独立组件时使用此模型。当项目已有既定库时,应使用其适当的原语调整行为,而不是重复其按钮、标题或表单控件。保留所需的语义和行为,同时调整示例的包装器和类。
对于基于组件框架(例如 Lumos)构建的项目,请使用该框架的组合和样式;其原语可能被导入。独立的零站点导入规则和命名空间回退令牌不会覆盖所选框架自身的契约。
可移植组件模型
对于独立的可移植组件,请遵循以下五条规则。对于所选框架,保留行为要求并遵循其组合/样式契约,如上所述:
- 零外部/站点导入:组件文件不得导入站点布局、按钮或第三方客户端 UI 库。唯一列出的例外是 SelfHostedMap,它需要地图渲染器和切片读取器;其规范列出了所需的包,并且仅在获得所有者批准后才安装。
- 带回退的命名空间 CSS 变量:绝不要在组件样式表中直接引用主机令牌(例如
var(--color-brand))。始终写成var(--comp-property, <fallback>)。 - 作用域 BEM 与 CSS 逻辑属性:使用 BEM 类命名(
.c-theme-toggle、.c-accordion__summary)和逻辑属性(inline-size、block-size、padding-inline、margin-block)。 - 幂等的原生 JavaScript 渐进增强:脚本必须在加载时执行,并能在 Astro 视图过渡激活时干净地重新附加(
document.addEventListener('astro:after-swap', init))。 - 触摸目标与 ARIA:按钮和交互控件必须具有最小
44px(2.75rem)或48px(3rem)的触摸目标、适当的 ARIA 状态(aria-expanded、aria-selected、aria-pressed、aria-controls)以及键盘焦点环。
组件索引
| 组件 | 规范文件 | 用途 | 关键契约 |
|---|---|---|---|
| 1. ThemeToggle | components/theme-toggle.md | 亮/暗主题切换器,零 FOUC,SVG 变形 | <head> 内联脚本 + OKLCH 令牌映射 |
| 2. MobileNavDrawer | components/mobile-nav-drawer.md | 无障碍的下滑式导航抽屉,带动画汉堡菜单按钮 | 48px 触摸目标,Escape 键关闭,无 GPU 模糊延迟 |
| 3. SearchBar | components/search-bar.md | 客户端搜索输入框,带清除按钮和实时朗读器 | rAF 防抖(最佳 INP),aria-live,自定义事件协调器 |
| 4. FilterChips | components/filter-chips.md | 多分类筛选药丸条,带可访问的按下状态 | role="group",aria-pressed,自定义事件协调器 |
| 5. SearchFilterBar | components/search-filter-bar.md | 组合 SearchBar 和 FilterChips 的复合模式 | 预组合的堆叠工具栏,多列 flex 布局,共享作用域 |
| 6. AccessibleModal | components/accessible-modal.md | 使用原生 HTML5 <dialog> 的无障碍对话框叠加层 | 原生顶层,背景点击,Escape 键,焦点恢复 |
| 7. AccessibleAccordion | components/accessible-accordion.md | 使用平滑 CSS 网格展开的原生 FAQ/手风琴组件 | 原生 <details name="...">,0 KB JavaScript |
| 8. AccessibleTabs | components/accessible-tabs.md | 用于定价层级或多面板视图的 WAI-ARIA 标签切换器 | 键盘箭头导航(Left/Right/Home/End) |
| 9. SocialShare | components/social-share.md | 隐私优先的社交分享栏,使用 Web Share API | 100% GDPR 安全,0 跟踪器,静态意图 URL |
| 10. TableOfContents | components/table-of-contents.md | 长文章目录,带滚动监听 | IntersectionObserver 标题跟踪,与文章布局配对 |
| 11. AccessibleCarousel | components/accessible-carousel.md | 用于评论、卡片或 Logo 的原生 CSS 滚动对齐滑块 | 原生触摸滑动(0 KB JS),可访问的上一步/下一步按钮 |
| 12. InfiniteTicker | components/infinite-ticker.md | 纯 CSS 无限 Logo/文本跑马灯,悬停/聚焦时暂停 | 符合 WCAG 2.2.2,使用 aria-hidden 隐藏重复轨道 |
| 13. StatsCounter | components/stats-counter.md | HTML 优先的指标计数器条,带缓出的滚动动画 | 0 CLS,静态 HTML 回退,IntersectionObserver |
| 14. AddToCalendar | components/add-to-calendar.md | “保存日期”链接,指向构建时生成的 iCalendar 文件 | 同源静态 .ics + <a download>,有效 RFC 5545,0 KB JS |
| 15. SelfHostedMap | components/self-hosted-map.md | 根据自托管 PMTiles 提取物显示地点列表的地图;编号图钉与列表匹配 | 懒加载 MapLibre,HTTP 206 切片路由,带文本替代的 aria-hidden 地图,跟随主题。需要 npm 包(所有者批准) |
| 16. ClickableParent | components/clickable-parent.md | 整张卡片/行通过一个真实的链接或按钮变为可点击 | ::after 拉伸点击区域,次要控件提升,焦点环在容器上,0 KB JS |
| 17. Card | components/card.md | 包含媒体、元数据、正文和次要操作的内容卡片 | 标题优先的 DOM,headingLevel 属性,容器查询布局,链接时符合 ClickableParent 契约 |
未编录组件决策门(人工决策点)
当项目需求或设计要求使用上述 17 个编录组件中未包含的交互式 UI 组件时(例如:前后对比图像滑块、日期选择器、范围选择器、步骤向导或自定义媒体播放器):
[!IMPORTANT] 停止并提问:代理绝不能静默安装客户端框架库(React、Vue、Svelte)、导入未经审查的第三方 npm 包,或留下临时文本占位符块(例如
<div class="c-placeholder">)。代理必须暂停并向项目所有者展示三种不同的选择:
- 选项 A:自定义原生组件(推荐):编写一个符合下方定制组件回退契约的、零依赖的
.astro组件。- 选项 B:静态 HTML 基线:实现一个完全静态的语义布局(例如并排图像或标准堆叠卡片),使用 0 KB JavaScript,完全省略交互式增强。
- 选项 C:目录模式适配:评估现有目录模式是否可以满足需求(例如将
AccessibleModal适配为图像灯箱,或将AccessibleTabs适配为步骤面板视图)。
定制组件回退契约
当项目所有者批准构建未编录的组件时(选项 A),实现必须满足以下六条规则;对于框架适配,对于第 3-4 项,请使用框架的组合、类命名和令牌规则:
- 语义 HTML 优先:在编写 JavaScript 之前,先将界面锚定到标准 HTML 元素(例如使用
<input type="range">作为滑块,<details>用于展开,<dialog>用于覆盖层)。 - 零外部运行时依赖:零客户端框架运行时(无 React/Vue),且无第三方 UI 组件包。仅在浏览器交互确实需要时,才编写纯原生 TypeScript/JavaScript。
- 作用域 BEM 与 CSS 逻辑属性:使用标准 BEM 命名(
.c-block、.c-block__element)和流相对布局属性(inline-size、block-size、padding-block、margin-inline、inset)。 - 带回退的命名空间令牌:绝不要硬编码十六进制值。使用主机语义令牌或带合理回退的命名空间自定义属性(
var(--comp-color, var(--color-primary)))。 - 无 JavaScript 渐进增强:当客户端 JavaScript 被禁用时,组件必须保持可读、可访问和可操作。使用
@media (scripting: none)或原生 CSS 回退布局(例如并排或堆叠视图),以确保没有内容被隐藏。 - WCAG 2.2 AA 合规:保持最小
44px(2.75rem)的触摸目标、高对比度的可见:focus-visible环、清晰的可访问名称(aria-label、<label>),并确保 0 个 axe-core 违规。
代理选择性加载指南
当用户要求实现或编辑上述某个组件时,不要读取整个目录。相反,只读取所需的特定组件文件(例如 components/accessible-carousel.md)。
集成验证
在构建/部署的输出上验证所选规范:语义和名称、键盘/焦点行为、状态转换、无 JavaScript 处理、重复初始化以及主机令牌映射。独立的打包规则仅适用于独立组件;框架可复用其原语。运行项目配置的检查/构建/测试,并披露不可用的检查或覆盖率缺口。44px 是此库的目标大小约定,而非 WCAG AA 阈值本身。
原生的 select 弹出菜单仍然是浏览器/操作系统 UI,除非所有者批准自定义组合框的权衡。