CSS这玩意儿,写了几年回头看,发现真正高频用到的属性也就那么几十个,但偏偏是这些基础属性,组合起来能玩出无数花样,也最容易踩坑。这一篇是“css常用功能总结”系列的第二篇,重点聊常用元素属性,涵盖布局、文字、动效、选择器这些日常必碰的场景。文章里的代码我都会配上注释,适合有一定HTML/CSS基础、想系统补全知识点的前端初学者,也适合写了几年CSS但老在兼容性上栽跟头的同学。
系列第一篇我把CSS的优先级、盒模型、定位几个底层概念过了一遍,这次直接落到元素属性上。我的习惯是:先在一个真实场景里看这个属性解决什么问题,再谈语法和参数,最后复盘踩过的坑。这样学完的东西才是能上线的,而不是背了一堆文档。
1. 布局篇:Flex与Grid的实战选型
1.1 Flex布局子元素宽度自适应的核心写法
Flex现在是布局的默认选项,但很多人只会用 display: flex 加 justify-content,一旦遇到子元素宽度自适应就懵。其实关键全在 flex 这个复合属性上,它由 flex-grow、flex-shrink、flex-basis 三部分组成。举个例子,一个导航栏里三个菜单项要等宽均分,最省事的写法是:
css复制.nav {
display: flex;
}
.nav-item {
flex: 1;
}
flex: 1 是 1 1 0% 的简写,意思是:允许放大、允许缩小、基础宽度为0,三项内容最终平分容器宽度。这里有个很多人忽略的细节:如果把 flex 写成 1 1 auto,效果完全不同,因为 auto 的基础宽度会跟着内容走,内容多的项就会更宽,做不到真正的均分。想保持内容宽度但只在剩余空间里弹性分配,用 auto 反而更合适。
踩坑最多的是子元素里加了长文本或者图片后,Flex 子项死活不按预期收缩。这类问题九成是 min-width 默认值在作怪。Flex 项的 min-width 默认是 auto,意思是“至少不能小于内容宽度”,内容一长就撑破容器。解决办法是给子项加一句 min-width: 0,让它在必要时可以收缩到比内容更窄。这个坑在移动端适配里特别常见,卡片列表明明设了 flex: 1,还是横向溢出,先检查一下是不是少了这一句。
1.2 Grid布局与gap属性的配合
Grid 和 Flex 不是替代关系,而是互补关系:Flex 适合一维布局(一行或一列),Grid 适合二维布局(行列同时控制)。我写后台管理系统的时候,卡片网格布局用 Grid 是最顺手的:
css复制.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
这行代码的意思是:每一列最小220像素,最大等分容器宽度,能排几列就排几列,列与列、行与行之间保持16像素间距。以前没 gap 的时候,大家只能用 margin 加负值的方式模拟间距,不仅代码丑,换行时边缘还会多出多余间距。现在 gap 属性在 Flex 布局里同样生效,比如给弹窗的操作按钮区设置 gap: 8px,比挨个加 margin 干净得多。
需要注意,gap 在较老的浏览器里对 Flex 的支持不如对 Grid 那么稳定。如果是维护一个面向老版本移动端的项目,布局间距还是尽量用 margin 兜底,或者用 @supports 做特性检测,支持 gap 就用,不支持就退回老方案。我个人在项目里的习惯是:新项目直接上,老项目先看用户占比再决定要不要用。
1.3 原子化CSS与布局工具的取舍
最近“原子性css”这个词热度很高,本质是把样式拆成单一职责的类,比如 .flex、.p-4、.text-center,在 HTML 里拼类名完成布局。这类方案的优势是写起来快、代码量少,团队里不容易出现命名冲突;劣势也明显,HTML 会变得很长,遇到复杂交互时追踪样式来源比较费劲。
我的态度是:原子化CSS适合组件化程度高、样式模式固定的中后台项目;不推荐在需要大量自定义视觉、动画细节的营销页或官网里强行使用。因为这类页面的设计高度定制,原子类很难覆盖,最后还得写一堆内联样式或自定义 CSS,反而更乱。选型时别只看“时髦”,要看项目里重复的样式模式有多少,重复多的才值得用原子化。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文字与字体:从基础属性到渐变、竖排
2.1 字体族与font属性的坑
字体相关属性是每个页面都躲不开的,但越基础越容易踩坑。font-family 的取值顺序是有讲究的:先写西文字体,再写中文字体,最后写通用字体族。原因很简单,西文字体通常只包含拉丁字符,放到前面能优先匹配英文和数字,中文则落到后面的中文字体上。一个实践中比较稳的写法:
css复制body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
}
这样写能保证在苹果和安卓设备上都有不错的效果。很多人会顺手写一行 line-height,这里的单位也值得注意。line-height 取值不带单位的数字是最好的,比如 1.5,意思是当前字体大小的1.5倍,子元素继承时按自己的字体大小重新计算;如果写成 line-height: 150%,子元素继承的是一个固定计算值,字体变大时行高不会跟着变,容易造成文字重叠。
再有就是 font-size 别一直用像素值。根元素设置基准字号后,内部文字尽量用 rem,这样用户调整浏览器字体大小时,页面能整体缩放,对可访问性友好。我维护的一个老项目,全是 px 写死,后来接到一条“系统字体调大后页面文字不变”的反馈,排查半天才意识到这个历史债。
2.2 字体渐变的两条实现路线
“css 字体渐变”这个热搜词我特别理解,几乎每个营销活动页都想要这种效果。实现原理不复杂,核心是 background-clip: text,把背景裁剪到文字形状,再配合透明文字显示出来:
css复制.gradient-text {
background: linear-gradient(90deg, #ff6a00, #ee0979);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
这段代码对浏览器内核有要求,-webkit- 前缀目前还是建议保留,否则在老版本 Chrome 和 Safari 上会失效。另一种思路是用 mask 遮罩实现,把文字作为遮罩层,背景渐变放在文字下方,效果类似但兼容性处理方向不同。两条路线选哪条?我的建议是:只求视觉效果就选 background-clip,简单直接;如果还要在渐变文字上叠加描边、阴影等更多效果,mask 方案的可扩展性更好。
这里有个特别隐蔽的坑:color: transparent 之后,如果旁边有选中文本的操作,用户选中渐变文字时高亮效果会变得很奇怪。如果项目有复制文字的需求,建议加一层 ::selection 样式,把选中态颜色补回来,避免用户选中一大片却啥也看不见。
2.3 文字竖排与溢出省略
“css文字竖着排列”搜索量不小,多半是遇到了古籍排版、海报标题、菜单侧边栏这类场景。CSS 里控制文字方向的核心属性是 writing-mode,竖排其实一行就能搞定:
css复制.vertical-text {
writing-mode: vertical-rl;
}
vertical-rl 表示竖向书写、从右往左排列,符合中文传统排版习惯。字间距可以用 letter-spacing 调整,行与行之间的间距则用 line-height 控制。需要注意的是,竖排文字里数字和英文的显示方向可能会旋转,不同浏览器表现不一,涉及复杂排版时一定要在目标浏览器里过一遍。
再说“css超出显示...”也就是单行省略,常规写法是三件套:
css复制.ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
多行省略则用 -webkit-line-clamp,配合 display: -webkit-box 使用。这里有三个常见问题:一是省略号在 Flex 子项里经常失效,原因是子项默认 min-width: auto 导致容器一直被撑开,加 min-width: 0 可解;二是多行省略需要固定 line-height,否则不同字号下行数计算不准;三是 text-overflow 只对块级或行内块元素有效,直接用在 span 上不生效。
2.4 容器内文本定位的实战问题
“怎么调整css容器里的文本位置”这个问题搜得多,是因为垂直居中没有一个“标准答案”,不同场景最优解不一样。先说结论:如果容器是 Flex,垂直水平居中用 justify-content: center 加 align-items: center 就够了;如果遇到简单的单行文本,老的 line-height 等于容器高度的办法也还行,但只适合定高容器,文本一换行就露出原形;Grid 容器则可以简写为 place-items: center,一行搞定两件事。
实际项目里,文本定位经常是“兄弟元素”导致的偏移。比如图标和文字放在一行,文字总是偏上或偏下,多半是图标本身的基线问题,给图标加 vertical-align: middle 加上 display: inline-block 通常能解决。处理这类问题的排查顺序我一般是这样:先看容器本身有没有 line-height 干扰,再看子元素有没有 vertical-align 或 margin 影响,最后才考虑 Flex 或 Grid 的对齐属性是否写全。
3. 动效与交互:过渡、动画与心理感知
3.1 hover延迟关闭的实现思路
“css hover延迟关闭”是一个很典型的交互需求,常见于下拉菜单、悬浮提示框。用户想把鼠标移开以后,菜单不要立刻消失,给一个缓冲时间,方便从菜单项移动到子菜单。实现方式不复杂,关键在于 transition-delay 要同时配好两条路:
css复制.dropdown {
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease, visibility 0.2s ease;
transition-delay: 0s, 0s;
}
.parent:hover .dropdown {
opacity: 1;
pointer-events: auto;
transition-delay: 0.3s, 0.3s;
}
注意这里我把 visibility 或者 pointer-events 也一起过渡了。光控制 opacity 有一个致命问题:元素虽然看不见,但还挂在页面上,会挡住下面的点击事件。所以隐藏状态必须配合 pointer-events: none 或者 visibility: hidden,否则就是一个“透明拦截层”。
还有个容易忽视的点:鼠标经过菜单项到子菜单的路径中,如果中间有间隙,hover 会瞬间丢失,菜单会闪退。解决办法是让菜单和触发区之间的间距尽量小,或者去掉间隙、用 padding 代替 margin 制造视觉间距。这个细节在纯 CSS 方案里尤其重要,必要时只能改用 JavaScript 做延迟关闭逻辑。我通常的做法是:菜单简单就用 CSS 延迟,菜单层级多、间距大就直接上 JS 控制。
3.2 涟漪光圈扩散效果拆解
“css涟漪光圈扩散”是按钮点击反馈里非常流行的一种动效。原理其实不复杂:一个从中心向外放大的圆形,透明度从1逐渐降到0。纯 CSS 实现可以借助 ::before 或者 ::after:
css复制.ripple {
position: relative;
overflow: hidden;
}
.ripple::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 100px;
height: 100px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: translate(-50%, -50%) scale(0);
animation: ripple 0.6s ease-out infinite;
}
@keyframes ripple {
to {
transform: translate(-50%, -50%) scale(4);
opacity: 0;
}
}
这里的两个细节值得注意:一是必须给父容器加 overflow: hidden,否则扩散的圆会跑出按钮边界;二是动画尽量只动 transform 和 opacity,不要去影响 width、height 或 left、top,这些属性变化会触发重排,动画容易掉帧。如果是按钮点击触发一次而不是循环播放,可以配合 JS 在点击时动态添加一个 class,或者用 :active 状态触发动画,效果同样好。
3.3 波浪效果与金光特效的实现要点
“css波浪效果”在活动页、加载页、模块底部分隔处很常见。最简单的波浪可以理解为多个圆角弧线循环位移拼接。不要把它想复杂,一个纯色区块加两个重叠的半圆伪元素,再让它们左右来回移动,就能模拟出水面起伏:
css复制.wave {
position: relative;
height: 100px;
background: url("data:image/svg+xml,...") repeat-x;
animation: waveMove 6s linear infinite;
}
@keyframes waveMove {
from { background-position: 0 0; }
to { background-position: 100px 0; }
}
SVG 波浪路径可以到在线工具里生成,选一个合适的波形,重复平铺,再让背景位置循环位移,视觉上就是连续流动的波浪了。想更真实可以叠两层不同透明度的波浪,错开动画时长,层次感会明显很多。
“金光闪闪”特效也是搜索热词,实现思路通常是用一个线性渐变作为高光带,通过背景位的循环移动,模拟光线扫过的感觉。核心是 background-image 渐变加一个宽幅的 background-size,再让 background-position 从左侧移到右侧。这类特效要注意性能,渐变区域不要铺满整个页面,只放在文字或图标上就够了,否则移动端帧率会崩。
3.4 动效样式库与性能权衡
现在的动效样式库非常多,animate.css、hover.css、自带动效库的 Tailwind 插件等等,几行类名就能给页面加上不错的动效。但我基本只在原型阶段用它们,生产环境很少直接全量引入,原因很简单:动效库通常会把几十上百个 keyframes 全部打进 CSS,而一个页面真正用到的只有几个,浪费的流量不算大,但维护成本高,品牌定制时还要重新写。
性能方面,动效设计要时刻记住一个法则:只动画 transform 和 opacity。这两个属性由合成器单独处理,不会触发布局和绘制;而 width、height、margin、font-size 这些变化会触发布局,动画过程会频繁计算,移动端很容易出现卡顿。设计动效时先问一句:这个变化能不能用位移、缩放、透明度来表达?能,就全用 transform/opacity。
4. 选择器、伪元素与CSS变量
4.1 兄弟选择器的应用场景
“css div上一个兄弟元素”这句搜索词,其实指的就是兄弟选择器。CSS 里的兄弟选择器有两种:相邻兄弟是 A + B,通用兄弟是 A ~ B。很多人在意的是“上一个兄弟”——CSS 没法直接选“上一个”,逻辑上只能向后选,不能向前选。所以遇到要控制前一个元素时,通常要调整 HTML 顺序,比如把触发元素放在目标元素前面,再用兄弟选择器来控制。
兄弟选择器最经典的用法是表单状态联动。比如勾选自定义 checkbox 后,后面的标签文字变亮,HTML 结构上把 input 放在 label 前面,然后用 input:checked + span 选中旁边的文字,纯 CSS 就能实现状态切换。还有导航栏里鼠标悬停某个 tab 后,其他 tab 变暗,也可以用 .tabs:hover .tab { opacity: 0.5; } 配合 .tabs .tab:hover { opacity: 1; } 实现。这种“控制其余元素”的思路,配合 :hover 和 :checked 能做出不少完整交互,不需要写一行 JS。
如果项目支持现代浏览器,A:has(~ B) 这种反向选择语法已经可用了,它能直接选“前面有B的A元素”,能解决很多老方法要调 DOM 顺序的问题。不过 :has() 的兼容性在移动端 WebView 里还是有差异,生产使用前一定要查目标用户群的浏览器版本。
4.2 伪元素变量与mask遮罩
“css 控制伪元素变量”这个需求我遇到过很多次,典型场景是同一个组件在不同地方要显示不同图标或不同颜色。CSS 变量可以在伪元素里直接使用,配合自定义属性实现“一套样式,多处定制”:
css复制.card::before {
content: var(--badge-text, "默认");
background: var(--badge-bg, #333);
color: var(--badge-color, #fff);
}
使用的时候只要在 .card 上覆盖变量,伪元素的样式也会跟着变。这个技巧在写组件库、主题系统时特别有用,避免了为了改一个颜色复制一整套样式。
关于 mask 遮罩,它的核心作用是用一张图或一个渐变控制元素的显示区域,黑色部分显示、透明部分隐藏。常见应用是头像裁切、渐变消失的边缘、不规则形状的图片展示。一个实用示例是用 mask 制作上下渐隐的图片,让图片边缘柔和过渡到背景色,比单纯用 opacity 自然得多。不过 mask 属性的兼容性依然依赖前缀,而且不同浏览器对遮罩图像的定位规则有些差异,使用前最好统一测试。
4.3 CSS变量在主题切换中的联动
CSS 变量(自定义属性)是我现在写样式框架的第一选择,因为主题切换、动态配色都绕不开它。全局变量定义在 :root 里,组件内的颜色、间距、字体大小都引用变量,切换主题时只要改一组变量值,整个页面自动换肤:
css复制:root {
--bg-color: #ffffff;
--text-color: #333333;
--primary: #1677ff;
}
[data-theme="dark"] {
--bg-color: #1f1f1f;
--text-color: #e5e5e5;
--primary: #4d9fff;
}
body {
background: var(--bg-color);
color: var(--text-color);
}
切换时用 JS 给根元素设置 data-theme 属性即可,不需要重新加载样式表。这样做的好处是:变量具有继承性,子元素都能拿到,但局部覆盖也很方便,同一个组件在不同场景里可以长不一样。要注意的是,CSS 变量是运行时解析,过多依赖变量会让样式调试变得困难,比如 DevTools 里看到的颜色值是个 var(--xxx),就得去追定义来源。我习惯在变量命名上做高度语义化,比如 --color-primary、--spacing-md,用的时候一眼能看出意图。
5. 移动端与兼容性实战排查
5.1 小程序苹果底部兼容问题
“小程序苹果底部兼容css”是移动端开发的老大难,主要是 iPhone 全面屏的底部小黑条(Home Indicator)会遮挡固定定位的按钮、导航栏。解决办法是利用安全区属性:
css复制.fixed-bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
constant() 是老版本 iOS 的写法,env() 是新版,两个都要写,而且必须先写 constant() 再写 env(),否则老版本会因识别不了新函数而忽略后面的值,顺序反了会导致老机型上完全无效果。前提是页面 viewport 设置了 viewport-fit=cover,否则安全区值始终为0:
html复制<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
在小程序里,因为页面自带 WebView 适配,部分场景可以直接用小程序提供的 safe-area 相关样式变量,但如果是自己渲染的 H5 页面,上面这套方案依然是通用兜底。调试时可以打开 iPhone 模拟器,把底部横条模拟打开,确认按钮的 padding 是否生效。
5.2 移动端hover失效问题
“前端css pc 端的hover 在手机端怎么设置”这个热词背后其实是移动端交互模型和 PC 的差异。移动端没有真正的鼠标状态,点击元素时浏览器会把第一次触摸模拟成 hover,导致一个现象:点击按钮后,hover 样式会“黏”在元素上不消失,必须点击页面其他位置才恢复。这在 PC 端习惯用 hover 做反馈的页面上,体验非常糟。
处理方案是区分设备类型,利用媒体查询判断主输入方式:
css复制@media (hover: hover) {
.btn:hover {
background: #dedede;
}
}
这段代码意味着只有支持 hover 的设备才会应用悬停样式,触屏设备直接跳过。反过来,可以配合 @media (hover: none) 给移动端单独写触摸反馈样式,比如用 :active 状态做按下效果。如果是小程序这种微信内置浏览器环境,hover 的模拟行为更接近移动端浏览器,同样建议用这个思路来做兼容。
5.3 CSS压缩报错的定位思路
搜索热词里有一条专业技术报错:“error: css minification error: cannot read properties of undefined”。这通常不是业务代码写错了,而是构建工具在后处理阶段压缩 CSS 时,遇到无法解析的语法。常见原因有三个:一是 calc() 表达式里的运算符两侧没有空格,比如 calc(100%-20px),压缩器解析失败;二是某些 CSS 新语法或嵌套写法的浏览器兼容配置不对,压缩器不认;三是变量引用在作用域内未定义,压缩时无法计算。
排查这类问题的思路,我一般是三步走:第一,打开构建日志,定位报错对应的是哪个源文件,这一步能找到就成功了一半;第二,检查该文件里所有 calc() 表达式,给减号和加号两侧都加上空格;第三,看看是否有浏览器兼容配置,比如 browserslist 导致压缩器尝试转译新语法时失败。如果这些都没有解决,就把报错文件里最近改动的几个属性注释掉,二分法缩小范围,直到定位到具体声明的属性。
这类压缩报错在生产环境非常吓人,因为它直接让 CSS 构建失败,页面样式全丢。我的建议是:本地就开启压缩构建,不要等到发布流水线才暴露问题;另外在 CI 里增加 CSS 构建的冒烟测试,哪怕只构建一个入口文件,也能提前拦下一堆低级问题。
结尾
写了这么多,我自己最大的体会是:CSS 的属性并不难背,难点在于理解每个属性在布局、交互、性能、兼容性上的连锁反应。同样是 flex: 1,少写一句 min-width: 0 就可能让整个页面横向滚动;同样是字体渐变,忘了 -webkit- 前缀就可能在目标浏览器上变成透明文字。这些都是文档里不会主动提醒你的事,只能靠项目中一个个踩坑攒下来。
最后分享一个我个人坚持的习惯:写样式时多问一句“这段代码如果交给一个不熟悉这个项目的人,他能看懂吗?”CSS 不是越少越好,也不是越高级越好,而应该追求“意图清晰、改动局部、影响可控”。像变量命名、注释规范、浏览器兼容方案的取舍,短时间内看不出差距,项目大了之后就全是这些细节在撑场面。
