用@mixin统一控制菜单项状态最可靠,通过变量定义过渡、混合宏封装hover/focus/active样式,并用@if处理focus-visible;&嵌套确保选择器精准,响应式@media宜嵌套在模块内,可访问性需结构与js协同。

垂直菜单用 Sass 实现,核心不是“怎么写样式”,而是“怎么组织结构和复用逻辑”——直接用嵌套、变量和混合宏控制层级、状态和响应行为,比手写 CSS 快且不易出错。
如何用 @mixin 统一控制菜单项的悬停与选中状态
手写每个 .menu-item:hover 和 .menu-item.active 容易漏掉过渡、焦点访问或伪类兼容。Sass 的混合宏能收口所有交互逻辑:
- 定义统一的过渡时长和缓动:用
$menu-transition: 0.2s ease-in-out变量,所有状态切换保持一致 - 把悬停、聚焦、激活三者样式合并进一个
@mixin menu-item-state,避免重复写background-color、color、outline - 注意
:focus-visible要单独保留,否则键盘用户会丢失焦点样式;Sass 中可加条件判断:@if $include-focus { ... }
示例片段:
@mixin menu-item-state($bg: #007bff, $text: #fff) {
background-color: $bg;
color: $text;
outline: none;
&:hover,
&:focus {
background-color: darken($bg, 10%);
}
&:focus-visible {
outline: 2px solid #0056b3;
}
}
为什么用 & 嵌套比写长选择器更安全
垂直菜单常有三级以上嵌套(如 .menu > .submenu > .submenu-item),纯 CSS 写法容易误伤全局或漏加空格。Sass 的 & 确保上下文精准:
-
&__item生成的是.menu__item,不是.menu .menu__item—— 避免意外继承或权重爆炸 - 子菜单展开动画需依赖父级状态(如
.menu-item.is-open > .submenu),用&可清晰表达“当前元素下的子元素”,而不是靠猜测选择器顺序 - 警惕嵌套过深:超过 4 层就该拆成独立模块,否则编译后 CSS 选择器权重过高,覆盖困难
响应式折叠菜单时,@media 放在嵌套内部还是外部
放在嵌套内部更可控。比如移动端收起二级菜单,只需在 .submenu 块里加 @media (max-width: 768px),不会影响一级菜单的显示逻辑:
- 嵌套内写媒体查询,CSS 输出是局部生效的(如
.menu .submenu@media(max-width:768px){display:none}),避免全局污染 - 若把
@media提到最外层,所有子元素响应逻辑会被平铺,后期修改某一层级的断点会牵连其他部分 - 推荐用
$breakpointsmap 管理断点值,配合@include media-breakpoint-down(md)这类自定义 mixin,语义更清
如何让菜单支持键盘导航和屏幕阅读器
Sass 本身不处理可访问性,但结构化写法能减少遗漏:
- 用
[aria-expanded]控制子菜单显隐,Sass 中可封装成@mixin menu-toggle-aria,自动添加aria-expanded="false"和对应状态切换 - 禁用默认列表样式时,别只写
list-style: none—— 屏幕阅读器可能跳过无语义的<div>,必须保留 <code><ul><li></ul>结构 - 焦点管理相关样式(如
tabindex、inert)不能靠 Sass 生成,但可在注释中标明哪些 class 需要 JS 配合,例如:// .menu-item--has-submenu → JS 需绑定 keydown(Enter/Space)
真正难的不是 Sass 怎么写,是菜单的 DOM 结构是否允许键盘流自然移动、焦点是否按视觉层级顺序出现——这些得和 JS 逻辑对齐,光靠样式层解决不了。











