iter(g343): 校验红环改为聚焦时亮

This commit is contained in:
HIsheR
2026-10-27 02:25:59 +08:00
parent d0ef012290
commit d5e16027f8
3 changed files with 37 additions and 2 deletions
+1 -1
View File
@@ -585,7 +585,7 @@ const content = h(
section(
'sec-formlab',
'表单校验',
'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处——自绘控件(下拉 / 评分 / 开关)没有原生 input,焦点退到它的触发器,整块不可聚焦时落在字段容器上;onError 拿到不通字段数。错误文案带唯一 id 并被 aria-describedby 关联到控件,读屏除了「无效」还会念出具体原因。点「重置」逐项复位:原生输入框回初始值、错误态与汇总条一起清空,自绘控件(城市下拉)靠 formItem 的 reset 钩子回写信号。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行;字段传 span 可跨列(备注整宽)。',
'form + formItem:必填 / 长度 / 正则同步校验,validator 返回 Promise 即异步规则(提交会等待);全部通过才触发提交。校验不通过时顶部出现「有 N 项需要修改」汇总条(role=alert 会被读屏播报)并聚焦第一处——自绘控件(下拉 / 评分 / 开关)没有原生 input,焦点退到它的触发器,整块不可聚焦时落在字段容器上;onError 拿到不通字段数。错误文案带唯一 id 并被 aria-describedby 关联到控件,读屏除了「无效」还会念出具体原因。点「重置」逐项复位:原生输入框回初始值、错误态与汇总条一起清空,自绘控件(城市下拉)靠 formItem 的 reset 钩子回写信号。校验失败只把控件描红,危险色光环聚焦时才亮——常驻红环会让整片字段看着像一起报错。本例 columns: 2 走两列栅格,窄屏自动堆叠,按钮区始终整行;字段传 span 可跨列(备注整宽)。',
form(
{ columns: 2, submitText: '注册', resetText: '重置', onSubmit: () => formResult(`提交成功:${username()} / ${email()} / ${city()}`) },
[
+9 -1
View File
@@ -1866,7 +1866,7 @@ button.ua-step-dot:hover {
.ua-form-error:empty {
display: none;
}
/* 校验失败:控件描红 + 危险色光环(root 与包裹式控件的内部触发器一并覆盖) */
/* 校验失败:控件描红常驻(root 与包裹式控件的内部触发器一并覆盖) */
.ua-invalid,
.ua-invalid .ua-input,
.ua-invalid .ua-textarea,
@@ -1874,6 +1874,14 @@ button.ua-step-dot:hover {
.ua-invalid .ua-radio,
.ua-invalid .ua-slider-input {
border-color: var(--ua-color-danger) !important;
}
/* 危险色光环只在聚焦时亮:常驻 3px 红环会让长表单看着像整页报错,也会和相邻字段的环叠在一起 */
.ua-invalid:focus-visible,
.ua-invalid .ua-input:focus-visible,
.ua-invalid .ua-textarea:focus-visible,
.ua-invalid .ua-select-trigger:focus-visible,
.ua-invalid .ua-radio:focus-visible,
.ua-invalid .ua-slider-input:focus-visible {
box-shadow: var(--ua-focus-ring-danger) !important;
}
.ua-form-actions {
+27
View File
@@ -1700,6 +1700,32 @@ function checkFormReset(pkgs) {
return issues;
}
/*** 校验错误环:红环常驻会把长表单染成一片警报,也会与相邻字段的环叠加——环只该在聚焦时亮 ***/
function checkInvalidRing(pkgs) {
const issues = [];
for (const pkg of pkgs) {
if (pkg.type !== 'skin-dom') continue;
const cssPath = path.join(ROOT, pkg.dir, 'ui.css');
if (!fs.existsSync(cssPath)) continue;
const lines = fs.readFileSync(cssPath, 'utf8').split(/\r?\n/);
let hasFocusRing = false; // 聚焦态危险环是否已单独写过
lines.forEach((line, i) => {
if (!line.trim().endsWith('{')) return; // 卫语句:只从选择器收尾行起算(一选择器一行是仓库既有排版)
let top = i; // 选择器首行:往上吃掉所有逗号收尾的续行
while (top > 0 && lines[top - 1].trim().endsWith(',')) top--;
const selector = lines.slice(top, i + 1).join(' ');
if (!selector.includes('.ua-invalid')) return; // 卫语句:与本规则无关的块
let body = ''; // 规则体:拼到第一行收尾的 }
for (let k = i + 1; k < lines.length && !lines[k].trim().startsWith('}'); k++) body += lines[k];
if (!/focus-ring-danger/.test(body)) return; // 卫语句:本块不涉及危险环
if (/focus/.test(selector)) hasFocusRing = true;
else issues.push(`${pkg.name}/ui.css:${i + 1} 的「${selector.trim().slice(0, 60)}」常驻危险光环:校验失败但未聚焦的字段也挂着 3px 红环,长表单看着像整页报错`);
});
if (!hasFocusRing) issues.push(`${pkg.name}/ui.css 缺 .ua-invalid:focus-visible 的危险环:改完常驻环后聚焦态反而没有错误强调`);
}
return issues;
}
/*** 校验同义词表:目标 id 必须真实存在(改名后残留会让回退指到空气),同一个词不得归两组(歧义会让结果随表序漂移) ***/
function checkSynonyms(ids) {
const issues = [];
@@ -1784,6 +1810,7 @@ function cmdVerify() {
issues.push(...checkToastLive(pkgs)); // toast 必须有 live 区域与 alert 抢占
issues.push(...checkFormErrorLink(pkgs)); // 表单错误文案必须被控件关联
issues.push(...checkFormReset(pkgs)); // 重置按钮必须真复位
issues.push(...checkInvalidRing(pkgs)); // 错误环只该在聚焦时亮
issues.push(...checkSynonyms(new Set(pkgs.flatMap((p) => (p.docs?.components ?? []).map((c) => c.id))))); // 同义词表指向的组件必须真实存在
checkDarkTokens(issues);
checkTokenParity(issues);