学习前提
理解CSS选择器、状态和层叠;本课不要求Promise或异步编程。
本课完成后你能做什么
为hover、键盘焦点、禁用和错误状态设计一致样式,并把装饰与内容分开。
先理解机制
伪类选择处于某种状态或结构位置的元素,如:focus-visible、:checked和:nth-child;伪元素表示元素的某部分,如::before与::first-line。先问变化来自真实状态还是纯装饰,再选择机制。不能因为::before能显示文字,就把关键错误说明只写进content。
hover面向指针悬停,触屏不一定稳定存在;focus-visible用于提供合适的焦点提示。按钮的禁用状态应由disabled属性表达,而不是仅降低透明度。样式应跟随语义状态,避免看起来禁用但仍可提交的矛盾。
组合选择器时限定作用范围,如.course-button:hover,而不是全局button:hover改变所有弹窗和表单。:where的零特异性适合低优先级默认样式;复杂选择器应以可读性为先,遇到覆盖问题先检查层叠,不要立即追加!important。
一个组件至少检查默认、hover、focus、active、disabled五种状态。验证状态组合,例如键盘焦点时仍可看见轮廓,禁用按钮不会保留看似可点的反馈。用文字明确动作结果,颜色变化只是补充。
可运行示例
运行方式:将HTML示例保存为独立的.html文件并在浏览器打开;含脚本的示例使用现代浏览器。资源文件与多文件模块另按正文要求准备。
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Honeysuck practice</title></head><body>
<style>
.action{font:inherit;padding:.7rem 1rem;border:1px solid #176343;background:white;color:#174d37;border-radius:.5rem}
.action:hover:not(:disabled){background:#e5f2e8}
.action:focus-visible{outline:3px solid #785b00;outline-offset:4px}
.action:active:not(:disabled){transform:translateY(1px)}
.action:disabled{opacity:.55;cursor:not-allowed}
.note::before{content:"";display:inline-block;width:.5rem;height:.5rem;border-radius:50%;background:#176343;margin-right:.5rem}
</style><p class="note">Practice is available without an account.</p>
<button class="action">Start practice</button><button class="action" disabled>Already submitted</button>
</body></html>
推演与验证
鼠标悬停第一个按钮,背景变化;Tab聚焦时有独立轮廓;按下时只有轻微位移。第二个按钮不能正常激活。暂时删除CSS,文字仍要清楚表达可用状态和说明,证明关键内容不依赖装饰。
常见错误与边界
伪元素不是DOM节点,不能像真实按钮一样提供键盘行为。:nth-child按兄弟位置而不是某种业务顺序计算;过滤或插入记录后位置可能变化,不能用它表达“已完成课程”。
动手练习
增加一个带aria-pressed的收藏按钮,让视觉状态由属性选择器驱动。收藏与未收藏都必须有明确文本。
参考解法与理由
按钮点击时同步aria-pressed和文字,CSS只读取属性。不要只切换类名却忘记语义状态。检查重复点击和键盘Enter操作。