后端前端CRM人工智能AI Agent【免费下载链接】crmComp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.项目地址https://gitcode.com/gh_mirrors/crm48/crm点击查看免费下载导读本文以 Comp AI CRM 仓库中的 .agents/skills/better-accessibility/focus-and-keyboard.md 技能文档为主体系统讲解 Agentic CRM 前端apps/app与packages/ui在焦点指示、跳过导航、tabindex 治理、模态框焦点圈定与 ARIA APG 键盘交互模式上的工程规范。读完本文你将掌握一套可直接落地的键盘可访问性实现方案如何用:focus-visible兼顾美观与可见性、如何为复杂应用布置 Skip Link、如何用inert与现代dialog替代繁琐的手写焦点陷阱以及如何让自定义组件完整兑现 ARIA 角色承诺的键盘模型。为什么键盘可访问性对 Agent 型 CRM 尤其重要Comp AI CRM 是一个Agentic-first的开源 CRM界面包含大量会话面板、Agent 构建器apps/app/components/agent-builder/、数据表格、抽屉与对话框等复合组件。键盘用户含依赖辅助技术的用户完全无法使用鼠标点击因此任何一次交互都必须有清晰可见的焦点位置否则视线键盘用户迷路可跳过的重复导航否则每次进入主内容都要 Tab 过整个侧栏符合预期的 Tab 顺序与箭头键模型否则自定义小组件变得不可操作模态上下文下的焦点圈定与还原否则焦点会逃逸到背景页面。以下规范正是围绕这四件事展开仓库中的真实组件如agent-builder-sidebar.tsx、dialog.tsx、sheet.tsx、tabs.tsx可作为落地参照。Focus Rings只美化:focus-visible永不裸写:focus核心原则样式化:focus-visible而不是裸的:focus。浏览器只在键盘与辅助技术触发焦点时呈现:focus-visible而鼠标点击虽然同样触发:focus却通常不触发:focus-visible——因为鼠标用户的焦点位置是显然的。反过来永远不要写outline: none或 Tailwind 的focus:outline-none而不提供可见替代那等于删除了有视力的键盘用户的导航能力。优先级顺序从优到次保留浏览器原生焦点环只加outline-offset让它在背景上透气。原生指示会自动适配平台与forced-colors设置作者无需预测每一种背景色。当设计确实需要自定义焦点环时使用项目已验证的焦点 token并人工检查渲染结果。/* Best: keep the browser ring, just give it breathing room */ :focus-visible { outline-offset: 2px; } /* Custom ring when the design requires one: use the projects verified token */ :focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }// Tailwind: use the projects focus token or established focus-ring utility button classNamefocus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--focus-ring)] Save /button一个常见的错误是outline: 2px solid不写颜色——它会被渲染成currentColor但这并不自动无障碍焦点环可能跨越与文本自身背景颜色不同的区域组件填充、页面表面、图片、渐变、hover/selected 状态对比度会失效。因此token、品牌色或currentColor只有当针对其穿过的每一条边缘做过渲染检查后才可接受。forced-colors 与 group focus在forced-colors: activeWindows 高对比度下保留默认颜色调整或显式使用系统颜色如Highlight不要用forced-color-adjust: none冻结作者颜色除非该控件在冻结后仍然可感知。当一个包装容器需要在内部输入框获得焦点时亮起典型场景带图标的搜索框图标嵌在边框内用:focus-within把焦点样式作用到整个容器。仓库中的真实案例apps/app/components/agent-composer-frame.tsfocus-within:border-muted-foreground/60 focus-within:ring-1 focus-within:ring-ring/40输入框聚焦时整条 composer 边框点亮apps/app/components/agent-builder/share-chat-dialog.tsxfocus-within:ring-2 focus-within:ring-ring/60apps/app/components/landing/ask-card.tsx着陆页提问卡片同样用focus-within:border-ring。Comp AI CRM 中的既有实践可直接仿写在 Agent 构建器相关组件中项目已经系统性地采用focus-visible:ring-2 focus-visible:ring-ring/60这一组合例如apps/app/components/agent-builder/agent-builder-sidebar.tsxoutline-none ... focus-visible:ring-2 focus-visible:ring-ring/60apps/app/components/agent-builder/agent-history.tsx按钮同时使用outline-none focus-visible:ring-2 focus-visible:ring-ring/60apps/app/components/agent-builder/chat-chips.tsx图标按钮focus-visible:ring-2 focus-visible:ring-ring/60。注意这些写法都遵循了本文档的铁律outline-none清除默认环必定伴随focus-visible:ring-*可见替代环两者成对出现从不单独使用。Skip Link让键盘用户直接跳到主内容当重复导航或其他重复的chrome侧栏、页头位于主内容之前时第一个可获得焦点的元素应是Skip to content链接目标指向main idmain。它平时视觉隐藏聚焦时再显示.skip-link { position: absolute; inset-inline-start: -999px; } .skip-link:focus { inset-inline-start: 16px; top: 16px; }body a classskip-link href#mainSkip to content/a header…/header main idmain…/main /body两个配套细节页内锚点目标需要scroll-margin-top例如 sticky 页头下用scroll-margin-top: 80px否则跳转后目标会被吸顶元素遮住。Skip Link 的隐藏方式用inset-inline-start: -999px而非display: none保证它仍存在于可访问性树且可被聚焦。Tabindex 三原则与 Roving Tabindex三条铁律值含义与适用场景tabindex0把元素加入自然 Tab 顺序。仅用于非原生可聚焦的自定义交互元素tabindex-1只能被 JS 聚焦el.focus()。用于要移焦到的标题、模态容器、roving-tabindex 成员正数tabindex永远不要用。它会劫持整页 Tab 顺序正确做法是修正 DOM 顺序Roving Tabindex复合组件只占一个 Tab 停靠点复合组件tabs、菜单、工具栏、单选组整体只占一个Tab 停靠点激活项tabindex0其余全部tabindex-1箭头键同时移动焦点与那个0div roletablist {tabs.map((tab, i) ( button roletab tabIndex{i activeIndex ? 0 : -1} aria-selected{i activeIndex} onKeyDown{handleArrowKeys} // ArrowLeft/ArrowRight move activeIndex, wrapping {tab.label} /button ))} /div这个模式的意义在于Tab 键负责在组件之间移动箭头键负责在组件内部移动两者分工清晰屏幕阅读器用户不会迷失在几十个 tab 里。仓库中 UI 基础库提供了 packages/ui/src/components/tabs.tsx它封装了TabList/Tab原语业务侧应优先复用而非手写。焦点圈定与还原inert优先dialog最优模态框必须圈定焦点。现代做法是用inert属性把对话框背后的所有内容一次性从 Tab 顺序与辅助技术树中移除而不再需要逐个元素管理// On open document.getElementById(app-content).inert true; const dialog dialogRef.current; (dialog.querySelector([autofocus]) ?? dialog.querySelector(button, [href], input, select, textarea))?.focus(); // On close document.getElementById(app-content).inert false; triggerRef.current?.focus(); // always return focus to the element that opened it优先使用原生dialogshowModal()焦点圈定、背景inert、Escape 关闭全部免费获得。若自定义遮罩无法使用dialog则必须手工加上roledialog、aria-modaltrue并用指向标题的aria-labelledby提供可访问名称。无论哪种方式共同规则打开时聚焦第一个可聚焦元素对于破坏性确认弹窗应聚焦最不具破坏性的操作如取消而非危险按钮关闭时焦点返回触发器若触发器已不存在则移到最近的逻辑容器在对话框上加overscroll-behavior: contain防止内部滚动带动背景页面。仓库对照packages/ui/src/components/dialog.tsxComp AI CRM 的 UI 库将 Radix UI 的Dialog封装为packages/ui/src/components/dialog.tsx根组件DialogPrimitive.Root保留 Radix 的焦点圈定focus trap语义DialogOverlaydialog.tsx使用fixed inset-0 isolate z-50覆盖层DialogContentdialog.tsx自带outline-none与关闭按钮关闭按钮内层文字用sr-only隐藏、但保留给屏幕阅读器Button variantghost classNameabsolute top-2 right-2 sizeicon-sm XIcon / span classNamesr-onlyClose/span /Button这正是图标按钮必须配可访问名称的范例——视觉上只有叉号图标辅助技术上是一个可读的 Close 按钮。同类封装还包括 packages/ui/src/components/sheet.tsx抽屉与 packages/ui/src/components/alert-dialog.tsx确认对话框业务侧如apps/app/app/(app)/[slug]/companies/create-company-sheet.tsx统一通过SheetTitle/SheetDescription补齐对话框标题与描述避免无名对话框。键盘模式ARIA APG一个角色就是一个承诺原生元素自带这些行为自定义组件必须自行实现。给元素加上roletab用户就会期待完整的 tab 键盘模型——role 是一种承诺。WidgetKeysDialogTab/ShiftTab 在内部循环两端回绕Escape 关闭Tabs箭头键在标签间移动循环Tab 移出到面板Home/End 跳到第一个/最后一个Menu buttonEnter/Space/ArrowDown 打开并聚焦第一项ArrowUp 打开并聚焦最后一项箭头导航Escape 关闭并把焦点还给按钮Disclosure / accordion头部是button aria-expandedEnter 和 Space 切换ComboboxArrowDown 打开/移入列表Enter 接受Escape 关闭并回到输入框输入即过滤Listbox / radio group箭头键移动选择整个组只有一个 Tab 停靠点通用规则Escape 逐级关闭最后打开的东西先 tooltip再 menu最后 dialog箭头键负责复合组件内部的移动Tab 键负责组件之间的移动Tabs 选择激活模式面板即时渲染用自动模式箭头聚焦即切换面板切换代价高时用手动模式Enter/Space 才激活Enter 提交当前聚焦输入框所属表单在textarea中 Enter 换行⌘/CtrlEnter 提交。SPA 路由切换焦点与标题的迁移客户端导航不会自动重置焦点也不会自动播报任何内容。每次路由变化时更新document.title以匹配新上下文把焦点移到新视图的h1需要tabindex-1或main返回/前进时恢复滚动位置前进导航时滚动到顶部。Comp AI CRM 前端是 Next.js 应用apps/app存在大量[slug]动态路由如apps/app/app/(app)/[slug]/companies/page.tsx因此标题 焦点迁移必须作为路由层通用逻辑处理而非散落在各页面里。自查清单提交前的五步验证把本文档压缩为可执行的自查清单供实现与 review 时逐项勾选焦点可见性所有交互元素在键盘聚焦时有可见指示是否存在裸outline-none/focus:outline-none且无替代环跳过导航重复导航前是否有 Skip Link主内容是否为main idmainTab 顺序是否存在正数tabindex复合组件是否实现了 roving tabindex一个 Tab 停靠点 箭头键内移模态焦点对话框/抽屉打开时背景是否被inert或等价机制移除出 Tab 顺序关闭后焦点是否回到触发器是否提供可访问名称键盘模型自定义 widget 是否兑现其 ARIA 角色的完整 APG 键盘行为特别是 Escape 与箭头键延伸阅读技能文档原文.agents/skills/better-accessibility/focus-and-keyboard.md同一技能目录下的相关规范可继续查阅.agents/skills/better-accessibility/下的其他文档如数据边界、证据与身份匹配等 Agent 技能文档位于 apps/agent/skills/UI 基础库组件实现packages/ui/src/components/dialog.tsx、packages/ui/src/components/tabs.tsx、packages/ui/src/components/sheet.tsx业务侧可访问性写法实例apps/app/components/agent-builder/agent-builder-sidebar.tsx、apps/app/components/agent-composer-frame.ts赞分享后端前端CRM人工智能AI Agent【免费下载链接】crmComp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.项目地址https://gitcode.com/gh_mirrors/crm48/crm点击查看免费下载相关推荐ShareDrop无障碍焦点管理键盘导航与焦点陷阱实现ShareDrop无障碍焦点管理键盘导航与焦点陷阱实现 在现代Web应用开发中无障碍设计A11y已成为提升用户体验的关键要素。ShareDrop作为基于后端前端即时通讯Logoly无障碍模态框焦点陷阱与键盘关闭实现Logoly无障碍模态框焦点陷阱与键盘关闭实现 在现代Web应用中模态框Modal作为用户交互的重要组件其无障碍设计直接影响着使用体验。本文将以Log前端CANN驱动URMA设备计数APIdcmiv2\_get\_urma\_device\_cnta nameZH CN_TOPIC_0000002519017001 /a 函数原型a驱动开发人工智能CANN上一篇BepInEx IL2CPP启动失败3种解决方案从诊断到修复下一篇BepInEx IL2CPP启动失败深度解析从架构诊断到系统级修复创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
