agentleFS
OugeXOugeX/dsh-web-dev-mode/skills/auth-pages-spec/SKILL.md

auth-pages-spec

Skill0 starsChanged 15 days agoMITView on GitHub

登录、注册、找回密码、绑定手机等认证页面的完整交互与 UI 规范。包含登录页布局与元素顺序、回车提交与 loading 与防重复、失败模糊提示与失败次数限制与验证码触发、记住我时长、注册页字段顺序与验证码倒计时与密码强度与协议勾选、找回密码三步流程与逐步独立 URL、完成后强制重新登录、autocomplete 与无障碍、移动端适配与底部安全区域。当写登录页、注册页、忘记密码、验证码流程,或评审认证页面体验时使用。

The file

Files
SKILL.md

定位:认证页是全站第一印象,也是最容易做错的地方


一、登录页

1.1 两种布局,按场景选

布局适用尺寸
居中卡片内部系统、后台管理卡片宽 380–420,垂直居中
左右分栏对外产品、需要品牌展示左 55% 图/渐变,右 45% 表单(表单宽 360)

判断标准:登录页有没有品牌宣传价值?有 → 分栏;没有 → 居中卡片。

移动端一律:居中单列,表单占满宽度减 24px 边距,高度不足时允许滚动(不要固定居中导致小屏溢出)。

1.2 元素顺序(不要自创)

① Logo(高度 32–40)
② 一句话产品名 / 欢迎语(可选,14px 灰字)
③ 账号输入框(手机号/邮箱/用户名,按业务定一种)
④ 密码输入框(带显示/隐藏)
⑤ [记住我 □]              [忘记密码?]   ← 同一行,左右分布
⑥ 主按钮「登录」           ← 全宽,高 44–48
⑦ 分隔线「或」
⑧ 其他登录方式(微信/钉钉/SSO,可选)
⑨ 「还没有账号?立即注册」 ← 居中,12px 灰字

为什么「记住我」和「忘记密码」同一行:这两个是登录页最高频的辅助操作,分两行会拉长页面、打断视线。

1.3 交互细节(每一条都是踩过的坑)

项要求
回车提交表单内任意输入框按 Enter 触发登录
自动聚焦进页面聚焦第一个空输入框(账号)
账号记忆勾选「记住我」后回填账号,不回填密码
loading按钮变「登录中…」+ 禁用,防重复提交
失败不跳转停留在当前页,错误就地显示,不清空已填内容
失败后聚焦focus 到密码框并全选,方便直接重输
密码可见性眼睛图标切换,默认隐藏
Caps Lock 提示检测到大写锁定,密码框下方提示「大写锁定已开启」

1.4 错误提示规范(安全相关)

必须模糊,不能区分:

✅ 账号或密码错误
❌ 该账号不存在
❌ 密码错误,请重试

后两种可以被用来枚举已注册账号。

失败次数与验证码:

失败次数处理
1–2 次只提示「账号或密码错误」
3 次出现图形/滑块验证码
5 次锁定 10 分钟,提示「尝试过于频繁,请 10 分钟后再试」
10 次锁定 1 小时 + 记录 IP

验证码出现后不要自动消失,一旦触发,本次会话内保持。

1.5 「记住我」的时长

选项时长适用
7 天默认内部系统
30 天可选个人设备
不勾选关闭浏览器即失效(session cookie)—

不要做「永久记住」。

1.6 安全细节

  • 登录成功后 replace 跳转(navigate('/home', { replace: true })),避免后退回到登录页
  • 支持 ?redirect= 参数,登录后跳回原页面;redirect 必须校验是同域相对路径,防开放重定向
  • 密码框 autocomplete="current-password"
  • 不要自动填充密码(除非用户上次选择了记住)
  • 页面标题带产品名:「登录 · 某某系统」

二、注册页

2.1 单页 vs 分步

字段数方案
≤ 5单页(推荐)
6–10单页 + 分组标题
> 10 或需要审核分步(Steps),每步 3–4 个字段

默认单页——注册的转化率与步骤数强负相关。

2.2 字段顺序(按"付出成本递增"排)

① 手机号 / 邮箱        ← 最轻,先要
② 图形验证码           ← 防机器注册(可选,风控触发时才要)
③ 短信/邮件验证码      ← 需要等待,放中间
④ 密码                ← 需要思考,放后面
⑤ 确认密码            ← 紧跟密码(或用显示切换代替确认框)
⑥ 昵称(可选)
⑦ ☐ 我已阅读并同意《用户协议》《隐私政策》  ← 最后
⑧ 主按钮「注册」
⑨ 「已有账号?去登录」

「确认密码」可以省掉:用密码框的显示/隐藏切换代替,能减少一个字段。

2.3 验证码交互(最容易做错)

const [countdown, setCountdown] = useState(0)

// 发送按钮三态
countdown > 0        → 「60s 后重发」(disabled)
sending              → 「发送中…」(disabled)
otherwise            → 「获取验证码」(可点)

// 点击后
setSending(true)
await api.sendCode(phone)
setCountdown(60)
setSending(false)

四条规则:

  1. 先校验手机号格式,格式错不发请求
  2. 倒计时 60 秒,期间按钮禁用并显示剩余秒数
  3. 页面刷新后倒计时要恢复(存 localStorage 记录发送时间戳)
  4. 验证码输入框 autocomplete="one-time-code" + inputmode="numeric",iOS 能自动填充短信验证码

2.4 密码强度

弱:长度 < 8
中:≥ 8 且含两类字符
强:≥ 12 且含三类字符(大小写 + 数字 + 符号)

显示为三段进度条 + 文字,实时更新。不要只标红绿,要告诉用户差什么:

密码强度:中 —— 建议加入大写字母或符号

2.5 协议勾选(合规硬要求)

  • 协议默认不勾选(国内合规要求,不得默认勾选)
  • 不勾选时点注册 → 按钮不置灰,点击后提示「请先阅读并同意协议」,并高亮协议行
  • 协议链接 target="_blank"
  • 文案:「我已阅读并同意《用户协议》和《隐私政策》」

不要用置灰按钮——用户不知道为什么点不了。

2.6 注册成功后

方案适用
自动登录并跳首页推荐,转化最好
跳登录页并预填账号需要二次验证时
跳「完善资料」引导有资料补齐需求时

无论哪种,都要 message.success('注册成功')。


三、找回密码

3.1 三步流程,每步独立 URL

/forgot/verify    → 验证身份(手机号 + 验证码)
/forgot/reset     → 设置新密码
/forgot/done      → 完成

为什么每步独立 URL:

  • 可以刷新不丢状态
  • 可以前进/后退
  • 可以分享/收藏某一步(虽然很少用)

但要用 sessionStorage 记录"已通过验证",防止直接访问 /forgot/reset 跳过验证。

3.2 第一步:验证身份

① 手机号 / 邮箱(预填登录页输入的账号,如果从登录页跳来)
② 图形验证码(风控触发时)
③ 短信验证码 + 「获取验证码」
④ 下一步

必须防枚举:无论账号是否存在,都提示「验证码已发送」。不要提示「该手机号未注册」。

3.3 第二步:设置新密码

① 新密码 + 强度提示
② 确认新密码(或显示切换)
③ 提交

不要在这一步再要一次手机号——已验证过了。

3.4 第三步:完成

✓ 密码已重置
「请使用新密码重新登录」
[ 去登录 ]  ← 主按钮

必须做的事:

  • 服务端作废该用户全部会话(其他设备强制下线)
  • 前端清空本地 token
  • 跳登录页,不要自动登录
  • 提示:「其他设备的登录已失效」

3.5 邮件链接式找回(另一种流程)

如果走邮件链接:

  • 链接有效期 30 分钟,一次性使用
  • 链接带 token,访问后立即换 session,URL 里不要留 token(history.replaceState 清掉)
  • 过期页面要给出「重新发送」入口

四、通用规范

4.1 autocomplete(不做密码管理器就废了)

字段值
账号username
当前密码current-password
新密码new-password
验证码one-time-code
手机号tel
邮箱email

4.2 移动端适配

项要求
输入框高度48
字号≥ 16(防 iOS 聚焦放大)
主按钮高 48,全宽
底部按钮固定时加 env(safe-area-inset-bottom)
键盘类型手机号 inputmode="tel",验证码 inputmode="numeric"
页面高度100dvh,不要 100vh
表单容器padding: 24px,小屏可滚动

4.3 无障碍

  • 每个输入框有 <label>(可视觉隐藏但要有)
  • 错误用 aria-invalid + aria-describedby 关联错误文本
  • 焦点顺序符合视觉顺序
  • 颜色对比度 ≥ 4.5:1

4.4 加载与骨架

  • 认证页不用骨架屏——页面本身很轻,直接渲染
  • 提交用按钮 loading
  • 第三方登录回调时用全屏 loading(只在跳转回来自动登录时用)

4.5 页面级细节

  • <title>:「登录 · 产品名」
  • favicon 正常显示
  • 移动端 viewport 带 viewport-fit=cover
  • 深色模式(如支持)要有对应配色,不要白底黑字硬切

五、验收清单

登录页

  • Enter 可提交,进页面自动聚焦账号
  • 密码有显示/隐藏切换
  • 按钮提交中 loading + 禁用
  • 失败就地提示,不清空输入,focus 回密码框
  • 错误文案是「账号或密码错误」,不区分具体原因
  • 失败 3 次出现验证码,验证码触发后本次会话保持
  • 登录成功用 replace 跳转
  • ?redirect= 只接受同域相对路径
  • 「记住我」只记账号不记密码

注册页

  • 验证码按钮三态(可点/发送中/倒计时)
  • 倒计时 60s,刷新页面后能恢复
  • 验证码输入框有 autocomplete="one-time-code"
  • 密码强度实时提示且说明差什么
  • 协议默认不勾选,未勾选时点注册给出提示而非置灰
  • 注册成功后行为明确(自动登录或跳登录)
  • 手机号格式错误时不发验证码请求

找回密码

  • 三步各自有独立 URL,可刷新
  • 未通过第一步不能直接访问第二步
  • 第一步不暴露账号是否存在
  • 完成后作废全部会话,强制重新登录
  • 邮件链接 30 分钟有效且一次性

通用

  • autocomplete 全部正确
  • 移动端字号 ≥16、按钮高 48、有 safe-area
  • 每个输入框有 label 与 aria 关联
  • 页面标题含产品名

六、反面清单

  1. ❌ 登录失败提示「该账号不存在」(可枚举用户)
  2. ❌ 「记住我」把密码也记住
  3. ❌ 登录成功用 push 跳转,后退能回登录页
  4. ❌ redirect 参数不校验,造成开放重定向
  5. ❌ 注册协议默认勾选(合规风险)
  6. ❌ 协议未勾选时按钮置灰(用户不知道原因)
  7. ❌ 验证码倒计时页面刷新后重置(可无限发送)
  8. ❌ 找回密码第一步提示「该手机号未注册」
  9. ❌ 改密后不 invalidate 其他会话
  10. ❌ 移动端表单字号 14(iOS 聚焦会放大整个页面)
  11. ❌ 不写 autocomplete,密码管理器无法填充
  12. ❌ 登录页用 100vh 导致 iOS 底部被裁

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.