面试题-css篇
讲真,CSS 面试题是整个前端面试里最容易被低估的一块。很多人觉得 JS 框架、算法、工程化才是重头戏,CSS 随便背背选择器优先级就上考场了,结果一到手写布局、讲原理、排查样式 bug 的环节就翻车。我自己参与过不少校招和社招的面试,也当过被面试的人,一个很直观的感受是:CSS 考的不是你背了多少属性,而是你有没有真正理解浏览器是怎么把样式渲染出来的,遇到问题有没有成体系的排查思路。
这篇就把我在面试准备和实际项目中沉淀下来的 CSS 高频考点捋一遍。适合正在准备前端面试的同学,也适合工作了一两年想查漏补缺的同行。内容不追求面面俱到,但凡是写出来的,都是确认过大概率会考、且工作中真能用得上的东西。
1. 先搞清楚 CSS 面试到底在考什么
1.1 考点分布与考察逻辑
CSS 相关的面试题看似零散,实际上可以归成三大类:基础原理、布局实战、视觉效果。基础原理包括选择器、优先级、盒模型、单位、层叠上下文这些,属于“懂不懂”的问题;布局实战包括 Flex、Grid、浮动、定位、多列布局这些,属于“会不会用”的问题;视觉效果包括渐变、动画、过渡、滤镜这些,属于“能不能做出来”的问题。
从面试官的角度看,这三类考察的是不同的能力维度。
基础原理题考察的是你对 CSS 这门语言本身的理解深度。比如“em 和 rem 有什么区别”,表面上考单位换算,实际上考的是你有没有理解相对单位背后的“参照系”逻辑——em 参照的是父元素的 font-size,rem 参照的是根元素的 font-size。如果你只是机械地记住“em 相对父元素,rem 相对根元素”,而没有想过嵌套场景下 em 为什么会累计放大,那遇到“多层嵌套下 font-size 用 em 会发生什么”这种变体题,就很容易掉坑。
布局实战题考察的是你在真实项目中搭建页面的能力。面试官一般不会让你背 Flex 的属性列表,而是会给一个还原设计稿的场景,比如“左侧固定宽度 200px,右侧自适应剩余空间,你会怎么做”。这种题没有唯一答案,关键看你能否在几秒钟内锁定最合适的方案,并且说出方案的优缺点和替代方案。能流畅作答的人,说明平时写页面的时候是有思考的,不是拿个框架套模板。
视觉效果题考察的是你对 CSS 动画原理和渲染机制的理解。热词里提到的“涟漪光圈扩散”“流光边框”“数字加载动画”“字体渐变”都是这类题的常见变体。这类题的重点往往不在“能不能做出来”,而在“知不知道 transform、opacity 与 left、top 在动画性能上的差异”以及“动画结束后状态如何保持”等更本质的问题。
1.2 面试准备的核心方法
我见过不少候选人刷题的方式是“背答案”,把 Github 上的 CSS 面试题库从头到尾过一遍。这种方式不能说完全无效,但效率很低,因为面试官稍微换个问法就露馅了。
我自己更推荐的做法是“以题带学,以写代背”。拿到一道题,先不急着看答案,自己在本地写一个 demo,把涉及的属性全部调一遍,观察不同取值下的渲染差异,然后再总结成自己的话。这个过程走一遍,比你背十个答案都管用。比如你看到“css 鼠标移入事件”这个热词,对应的考点其实是 CSS 的:hover伪类和鼠标交互设计。你可以自己写一个卡片悬停效果,试一下transform: scale()和box-shadow的配合,再对比一下用 JS 的mouseenter/mouseleave实现有什么区别。这样一道题吃透,等于同时复习了伪类、过渡、变换、事件机制四个知识点。
另外强烈建议准备一个“错题本”。不需要多精致,一个 Markdown 文件就行。把自己在笔试或模拟面试中答错的题、理解偏差的概念、写代码时踩过的坑都记下来,每周复盘一次。我在准备跳槽的那段时间,靠这个错题本把很多模棱两可的知识点彻底理清了,比刷多少套题库都实在。
2. 高频基础考点:从选择器到盒模型
2.1 选择器与优先级:面试必问的第一道菜
“第1关:css 元素选择器”这个热词说明了很多人在初学阶段就是从元素选择器入门的,但面试考选择器从来不会只考“元素选择器怎么写”,而是会把选择器、优先级、继承、权重计算串在一起考。
元素选择器是最基础的一种,直接写标签名,比如div { color: red; }。它的优先级权重是 0,0,0,1,类选择器是 0,0,1,0,ID 选择器是 0,1,0,0,内联样式是 1,0,0,0。!important则在所有声明之上。
比较经典的面试题是“优先级相同的时候,后面的样式会覆盖前面的吗”,答案是会。很多初学者以为选择器优先级只跟选择器类型有关,忽略了“源代码顺序”这个因素。当两个声明的优先级完全一样时,后面出现的那个胜出,这个规则在面试中经常被拿来设置陷阱。
还有一道很经典的变体题:div .a .b和div.a .b哪个优先级高。第一眼看上去好像差不多,但拆开算一下权重就清楚了。前者是 1 个元素选择器加 2 个类选择器,权重是 0,1,2,1;后者也是 1 个元素选择器加 2 个类选择器,但注意后者的第一个类选择器是修饰在 div 上的(即 div.a),权重同样是 0,1,2,1。如果权重一样,就比谁在源代码中靠后。这种题考的其实是“你有没有养成拆解权重数组的习惯”。
实操中还有一个高频考点是:not()伪类。:not()本身不参与优先级计算,影响优先级的是它括号里的选择器。比如:not(.disabled)的权重等同于.disabled。很多人不知道这一点,结果写出来一个看似无害的选择器,却无意中把别的样式覆盖了。我在项目里就踩过这个坑:想给所有非禁用状态的按钮加 hover 效果,写了button:not(.disabled):hover,结果因为权重问题把原本的 disabled 样式覆盖了,调试了半天才发现是:not()的优先级在作怪。
2.2 盒模型:content-box 与 border-box 的纠缠
盒模型是 CSS 的地基,面试官特别喜欢在这个点上深挖。核心考点是两个:一是 content-box 和 border-box 的区别,二是如何用代码验证浏览器默认的盒模型。
content-box(W3C 标准盒模型)的宽度只包含 content,不包含 padding 和 border;border-box(IE 盒模型)的宽度则包含 content、padding 和 border。在实际布局中,如果使用 content-box,给一个元素同时设置宽度、左右 padding 和左右边框,它的实际渲染宽度会超出你预设的宽度,导致布局错位。这也是为什么现在主流的 reset 方案都会加上* { box-sizing: border-box; }。
面试官常见的追问是:“如果全局设置了 border-box,那 content-box 还有存在的意义吗?”答案是有的。有些场景下你需要精确控制内容区的尺寸,比如你做图片裁切、做文字溢出省略,或者对接第三方组件库时,这些库内部的样式可能基于 content-box 推算尺寸,贸然全局切换反而会造成偏差。这时候你的思路应该是针对局部容器重置box-sizing: content-box,而不是一刀切。
验证盒模型可以用浏览器 DevTools。打开开发者工具,选中一个设置了宽度、padding、border 的元素,看 Computed 面板里 width 的值——如果显示的是 content 的宽度,就是 content-box;如果显示的是包含 padding 和 border 的总宽度,就是 border-box。这个验证方法虽然简单,但面试时能现场演示出来,比干巴巴背诵定义要有说服力得多。
2.3 单位体系:em、rem、vw/vh 的选型逻辑
“css em”出现在热词列表里我一点也不意外,因为单位题几乎是每场面试必考的。em 的迷惑性在于它的“参照系”随着属性不同而变化:当用于font-size时,它参照的是父元素的font-size;当用于width、margin、padding等属性时,它参照的是当前元素自身的font-size。
举个例子,如果父元素的font-size是 16px,子元素的font-size: 1.5em算出来是 24px。如果此时子元素还有一个margin: 1em,那这个 margin 参照的是子元素自身的font-size,也就是 24px,而不是父元素的 16px。这个“双重参照”的特性很容易让人混淆,面试题也特别喜欢在这里挖坑。
rem 就好理解多了,它永远参照根元素(html)的font-size。只要根元素的字号不变,rem 计算出的值就是确定的,不会因为嵌套层级而累加。所以媒体查询适配、移动端适配方案里,rem 是比 em 更稳定的选择。而 vw/vh 是参照视口(viewport)尺寸的,1vw 等于视口宽度的 1%,适合做全屏适配。但要注意 vw 在处理滚动条宽度时会有偏差,100vw 会让页面出现横向滚动条,因为视口宽度包含了滚动条的宽度,而100%是相对包含块的。
如果面试官问“移动端适配你会选什么单位”,我的建议是:优先rem+ 动态根字号,或者直接用vw,具体看项目天花板。但不管选哪种,都要能说清楚“为什么不用 em”——因为它有累计放大的问题,嵌套层级一深,字号就不可控了。这个回答既展示了知识储备,又体现了你在真实项目中踩过坑的经验。
2.4 样式引入方式:为什么总有人问要不要写 style 标签
热词里有“css样式引入方式”和“css文件需要写style吗”,这两个问题看着基础,但在面试里问出来,考察的其实是工程化思维。
CSS 的引入方式主要有四种:外部样式表(<link>引入独立.css文件)、内部样式表(写在<style>标签里)、内联样式(写在元素的style属性里)和@import方式(在 CSS 文件里通过@import url(...)导入另一个 CSS 文件)。
先说“css 样式引入方式”这道题的完整答法。外部样式表是项目中最常用的,因为可以缓存、可以并行加载、方便维护。内部样式表适合页面级单次使用的少量样式,或者用于覆盖第三方组件的部分样式。内联样式优先级最高(不加!important的前提下),适合动态样式或必须强制覆盖的场景,但不建议大规模使用,因为没法复用、也没法缓存。@import用得最少,因为它在 CSS 文件加载完成后才去加载被引入的文件,会阻塞渲染,而且有性能问题。
再单独说“css文件需要写style吗”。这是一个小白经常问的问题,但面试中若被问到,考点其实是你有没有理解“<style>是 HTML 标签,.css文件是独立的样式表文件”这个边界。答案很明确:.css文件里直接写 CSS 规则就行,不需要写<style>标签。<style>是写在 HTML 页面里的容器标签,用来包裹内部样式表;外部样式表的内容会被浏览器当作 CSS 解析,如果你在.css文件里写了<style>,浏览器会把它当作普通的标签选择器来处理,样式直接失效。
我在面试中一般会把这个知识点跟工程化串起来讲:在大厂的项目里,你几乎看不到手写<style>的情况,因为组件化开发时样式跟着组件走,打包工具会把 CSS 抽离成独立文件,然后自动通过<link>引入。如果你还会用@import拼接多个 CSS 文件,建议尽早改成构建工具的方式——否则你会遇到“首屏加载白屏很久才开始显示样式”的尴尬局面。
3. 布局题是重头戏:五种布局方式全拆解
3.1 从传统到现代:五种主流布局方案对比
“html css 五种布局方式”这个热词对应的考点是:浮动布局、定位布局、Flex 布局、Grid 布局、多列布局(或 Table 布局)。面试里最常出现的问法是“你能列出几种布局方式,并说明各自适用场景”,这道题答得好不好,直接反映你项目经验的厚度。
我把五种布局的核心要点整理成表格方便记忆:
| 布局方式 | 核心机制 | 典型应用场景 | 常见坑点 |
|---|---|---|---|
| 浮动布局 | float: left/right使元素脱离文档流 | 图文环绕、旧版两栏/三栏 | 需要清除浮动(clearfix),父容器高度塌陷 |
| 定位布局 | position: static/relative/absolute/fixed/sticky | 弹窗、下拉菜单、固定导航、角标 | 定位基准混乱,absolute 的包含块判定 |
| Flex 布局 | display: flex一维排列(主轴/交叉轴) | 导航栏、列表、居中、等分布局 | 子项默认flex-shrink: 1,空间不足会压缩 |
| Grid 布局 | display: grid二维网格(行+列) | 卡片墙、仪表盘、复杂页面骨架 | 兼容性需确认,写法比 Flex 复杂 |
| 多列布局 | column-count: n内容自动分列 | 文本分栏、瀑布流(配合break-inside) | 阅读顺序是纵向的,不适合卡片式布局 |
面试里最常考到的是 Flex 和 Grid,因为这两种是现在实际开发中的主力。但浮动布局和定位布局也不能完全忽略,尤其是 absolute 和 relative 的配合,在很多组件实现里依然不可替代。
3.2 Flex 布局:主轴、交叉轴与常用属性的配合
Flex 布局的面试题属于“背书容易,实战见真章”的类型。常见的基础题是“垂直水平居中怎么写”,很多人会背出justify-content: center; align-items: center;,但面试官如果接着问“这个容器的flex-direction是 row 还是 column”,有人就会卡住——因为默认是 row,水平排列,所以justify-content控制的是水平方向,align-items控制的是垂直方向。如果你把flex-direction改成 column,主轴变垂直,两个属性的控制方向就对调了。
这个知识点在面试中的变体题是:flex: 1到底是什么意思。要答好,得拆成三个子属性来说:flex-grow(放大比例,默认 0)、flex-shrink(缩小比例,默认 1)、flex-basis(基础尺寸,默认 auto)。flex: 1实际上是flex-grow: 1; flex-shrink: 1; flex-basis: 0%的简写,表示“容器剩余空间全部分配给这个子项”。很多人只记住了flex: 1是“占满剩余空间”,但说不清为什么有时候设置flex: 1后子项宽度没有达到预期——因为父容器还有min-width: auto的默认约束,子项内容如果太长,会被撑开。这时候你需要给子项加min-width: 0才能让它真正收缩。
Flex 布局还有一个高频考点:gap属性。旧版本里 Flex 子项之间的间距靠margin来实现,但用 margin 会出现“最后一个元素多了边距”的问题,需要额外处理。现代浏览器基本都支持 Flex 容器的gap了,gap: 16px就能统一处理间距。面试时主动提到这一点,能体现你关注到了 API 的变化趋势。
3.3 Grid 布局:二维控制与三等分卡片布局实例
Grid 是现在布局题里最能体现功底的考点。它的核心优势是“二维控制”,同时管理行和列。经典面试题是“用 Grid 实现一个每行三列、自适应用的卡片列表,间距 20px,最后一行不满三列时卡片左对齐”。
标准写法是:
.card-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; }这段代码背后的原理值得展开说。repeat(auto-fill, minmax(280px, 1fr))的意思是:让浏览器自动决定放多少列,每列的最小宽度是 280px,如果容器还有剩余空间,就重新分配给所有列(1fr表示等分剩余空间)。这样写能同时实现“响应式换行”和“等宽伸缩”,比传统的媒体查询断点方式优雅得多。
但注意一个坑:auto-fill在容器宽度不够时,如果最后一行的元素不够三个,剩余的网格轨道会被空出来,导致卡片不左对齐。如果你希望占满整行而不是保留空轨道,应该用auto-fit。auto-fill和auto-fit的区别就在于“是否折叠空轨道”,这是 Grid 面试题里非常经典的陷阱。你可以用grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))解决左对齐问题。
3.4 定位布局里的经典实战:容器内文本位置调整
热词“怎么调整css容器里的文本位置”其实可以拆成好几层来答,但面试官通常期待你把定位、对齐、内边距三条路都说到。
最简单的方式是用text-align(水平居中/左/右)和line-height(垂直居中,适用于单行文本)。这种方式只能处理文本在普通流中的对齐,一旦容器高度变化,line-height方案就失效了。
第二种方式是 Flex 配合align-items: center; justify-content: center;,这是现代开发中最推荐的。容器内部多行文本也能居中,且不依赖行高。
第三种方式是定位布局,适用于“容器内某块文本放在特定位置”的场景。原理是给容器设置position: relative,给文本块设置position: absolute,然后用top/right/bottom/left或transform: translate()来移动。这里有一个高频考点:当文本块用left: 50%; top: 50%定位时,它所处的位置是“容器中心点的左上角”,为了让文本块自身也居中,需要加transform: translate(-50%, -50%)。这就是“css平移”这个热词的面试出处——transform: translate()既用于居中,也用于浮层微调,还用于动画移动。
4. 进阶效果与动画:从字体渐变到涟漪光圈特效
4.1 字体渐变与流光边框:视觉效果的实现原理
“css 字体渐变”和“流光边框 css”都是视觉效果类的高频题,也是我面试中非常喜欢考的实操题,因为这类题能直接看出候选人写 CSS 的熟练度。
字体渐变的实现思路是先把文字颜色设为透明,然后给文字用background渐变填充,最后用background-clip: text把背景裁剪到文字形状。核心代码:
.gradient-text { background: linear-gradient(90deg, #ff6a00, #ee0979); -webkit-background-clip: text; background-clip: text; color: transparent; }这段代码里有几个关键点值得注意。一是background-clip: text目前仍然有很多浏览器需要加-webkit-前缀;二是color: transparent必须放在最后,否则文字颜色会盖住背景;三是如果你给这个元素设置了border,要注意background-clip的裁剪范围默认包含 border 区域,必要时用padding-box或content-box修正。
流光边框的思路比字体渐变复杂一些,常见的做法有两种。第一种是“渐变边框 + 背景遮罩”方案:给元素加一个渐变背景的border-image,但border-image不支持圆角,这是个硬伤。第二种是“伪元素方案”:在元素下面垫一层带渐变背景的伪元素,再让主元素盖在它上面,四周露出一点边;配合animation移动渐变的background-position,就能做出流转效果。
伪元素方案的核心代码大致是这样:
.box { position: relative; border-radius: 12px; overflow: hidden; } .box::before { content: ""; position: absolute; inset: -2px; border-radius: inherit; background: linear-gradient(90deg, #00dbde, #fc00ff, #00dbde); background-size: 200% 100%; animation: flow 3s linear infinite; } .box::after { content: ""; position: absolute; inset: 2px; border-radius: inherit; background: #fff; } .box .content { position: relative; z-index: 1; }inset: -2px让伪元素比主元素大一圈,露出两像素的伪元素轮廓,视觉上就是边框。background-size: 200% 100%配合background-position的动画就能让渐变流动起来。面试时能写出这个方案,并且说明::before和::after各承担什么职责,基本就能过关。
4.2 涟漪光圈扩散与数字加载动画:CSS 动画的性能与细节
热词“css涟漪光圈扩散”很像是“波纹按钮”或“水波纹反馈”的变体,经典实现思路是:一个position: relative的按钮,点击时在按钮内部生成一个从中心向外扩散的圆形光圈,光圈逐渐变大变淡。
这种效果可以用@keyframes配合transform: scale()和opacity来实现。核心思路是:用一个绝对定位的圆形伪元素,初始transform: scale(0),动画结束时transform: scale(4),同时opacity从 0.5 降到 0。这里有一个非常重要的考点:为什么用transform和opacity做动画,而不用left、top、width、height?答案是因为transform和opacity不会触发重排(reflow)和重绘(repaint),会直接提交到合成器处理,性能上要好得多。如果你用left从 0 改到 100px,浏览器每帧都要重新计算布局,掉帧几乎是必然的。
数字加载动画效果则是一个“伪数字滚动”的需求。常见的实现方式有两种:一种是 JS 定时器每帧更新数字,配合 CSStransition做数字变化的过渡;另一种是只靠 CSS 的@property注册自定义属性配合counter-reset来实现数字滚动,但兼容性有限。面试时如果时间充裕,可以两种都提一下,然后说明你推荐哪种。
我比较推荐的做法是 JS 配合 CSS 的 requestAnimationFrame +transform来做“数字跳跃”效果,因为纯 CSS 方案在 Safari 老版本上兼容性不好,项目里容易埋雷。核心逻辑是:用一个requestAnimationFrame驱动数字从 0 增长到目标值,增长的过程中用 CSS 的filter: blur()或transform: translateY()做模糊和位移过渡,让数字滚动显得更真实。
4.3 鼠标移入事件与倒计时:CSS 与 JS 的协作边界
热词“css 鼠标移入事件”和“js css 倒计时”放在一起看,恰好引出一个面试高频边界题:哪些交互用 CSS 实现,哪些必须用 JS?
鼠标移入事件在 CSS 里的对应物是:hover伪类。很多悬停效果,比如按钮变亮、卡片上浮、图片缩放,甚至下拉菜单的展开,都可以用纯 CSS 实现。:hover配合transition可以做到很顺滑的交互反馈,而且零 JS 开销。但面试官可能会问::hover能覆盖移动端吗?答案是不能完全覆盖,因为在触摸设备上没有“悬停”状态,:hover的行为是第一次点击触发,第二次点击才真正执行点击逻辑,这是移动端的一个经典坑。
倒计时的实现一般走 JS 路线,因为倒计时是时间驱动的,CSS 没有“每秒钟改变一次内容”的能力。但倒计时的动态样式部分可以用 CSS 来做,比如倒计时数字的闪烁、警告变色(剩余 5 秒变红)、进度条的宽度变化(通过transition平滑缩短)。这其实就是前端开发中“样式归 CSS,逻辑归 JS”的分工原则。面试时能用这个思路答题,比单纯给一段按钮代码惊艳得多。
我实际项目里常用的一种倒计时尚方案是:JS 每秒钟计算出剩余秒数,更新文本;CSS 负责闪烁动画。关键代码示意:
.countdown-warning { animation: blink 1s step-end infinite; color: #e74c3c; } @keyframes blink { 50% { opacity: 0.4; } }setInterval(() => { const remain = endTime - Date.now(); if (remain <= 5000) { el.classList.add("countdown-warning"); } el.textContent = Math.max(0, Math.ceil(remain / 1000)); }, 1000);step-end让闪烁没有中间过渡帧,视觉上更接近警示灯的“一明一灭”效果,比linear更像真实倒计时。
4.4 CSS 元素选择器进阶与原子性 CSS 的思路
热词里出现了“第1关:css 元素选择器”和“原子性css”,这两个放在一起其实很有意思——一个是 CSS 最基础的知识,一个是 CSS 组织方式的高级话题,但底层逻辑是相通的:选择器的设计直接影响样式复用和维护效率。
元素选择器是入门第一课,但在面试中常考的是组合用法,比如ul li(后代选择器)和ul > li(子选择器)的区别。前者匹配所有后代,后者只匹配直接子元素。如果渲染多层嵌套列表,用错了选择器,样式会被层层继承,出现“不该有的缩进”或“不该有的背景色”。我在真实项目里就见过:写导航的时候用了ul li给所有 li 加了下边框,结果二级菜单的 li 也加了边框,看起来就像表格线一样乱。
原子性 CSS 则是另一套组织思路,它的核心是“把每个样式声明拆成独立的小类”,比如.text-center { text-align: center; }、.mt-8 { margin-top: 8px; }、.flex { display: flex; }。Tailwind CSS 就是这种思想的集大成者。面试时如果你提到自己在项目里用过原子性 CSS,面试官大概率会追问:用原子性 CSS 的好处和坑在哪里。
好处是显而易见的:样式类和 HTML 结构一一对应,改样式不用到处找 CSS 文件;类名复用率高,打包体积可以控制。坑也很明显:HTML 里会堆一大堆类名,可读性下降;如果团队里没人统一规范,很容易出现“同一个间距有人用mt-8、有人用mt-2、有人直接写style="margin-top: 6px"”的混乱。我的建议是,在组件化项目中,原子性 CSS 和传统语义化 CSS 可以共存:组件内部用传统 CSS 管理复杂状态样式,组件之间用原子类做统一间距和布局,这样既灵活又可控。
5. 面试中的高频追问与避坑指南
5.1 五个最容易答错的 CSS 细节
这里我整理了面试中非常容易翻车的五个细节,每一个都是我在真实面试中见到候选人卡壳的高频点。
第一个是width: 100%和width: auto的区别。width: 100%是“撑满包含块的 content 区域”,但它不考虑 padding 和 border,所以如果你同时设置了 padding,内容会溢出。width: auto则会让元素自动填满包含块,同时包含 padding 和 border 的尺寸计算,不会溢出。面试官往往通过这个题考察你有没有被浏览器默认样式坑过。
第二个是display: none与visibility: hidden的区别。前者元素不生成、不占位、不响应事件;后者元素隐藏但仍占位,且仍然可以触发事件——visibility: hidden的元素是可以响应 JavaScript 事件的。很多人以为隐藏了就不能点了,这个认知在开发弹窗蒙层、隐藏按钮时特别容易踩坑。
第三个是position: fixed的包含块问题。很多人以为fixed永远相对视口定位,但实际上,如果祖先元素设置了transform、perspective或filter等属性,fixed的包含块会变成这个祖先元素。这就是为什么你写了一个回到顶部的悬浮按钮,放在某个加了transform的容器里,就莫名其妙跑到别的位置去了。
第四个是line-height的继承问题。line-height的值如果是具体数值(比如 24px),子元素直接继承这个值;但如果写成比例值(比如 1.5),子元素会继承这个系数,并结合自身字号重新计算。这两种继承方式带来的显示效果差别很大,面试里常考的就是“line-height: 1.5 和 line-height: 150% 有什么区别”。
第五个是 margin 折叠问题。垂直方向上,相邻兄弟元素的 margin 会合并,取较大值。比如第一个元素margin-bottom: 30px,第二个元素margin-top: 20px,最后它们之间的间距是 30px,而不是 50px。父子元素的 margin 在某些情况下也会折叠,比如父元素没有 padding 或 border、没有 overflow、没有 BFC 时,子元素的 margin-top 会把父元素一起顶下来。
5.2 排查样式问题的标准流程
面试官经常在最后加一道开放题:“如果页面里有一个元素样式不对,你怎么排查?”这道题没有标准答案,但答得好不好,直接体现你的工程经验和调试能力。
我一般会按这个流程来走。第一步,打开 DevTools 的 Elements 面板,选中目标元素,看 Computed 面板里最终生效的样式值。如果某个属性不是你预期中的值,点进去看它的来源是哪个选择器,是不是有更高优先级的样式覆盖了你的声明。
第二步,检查元素本身的盒模型。看 Content、Padding、Border、Margin 四个区域的实际尺寸是否符合预期。很多时候样式“看着不对”,其实是没有区分 content-box 和 border-box,导致宽度计算超出了容器。
第三步,检查父级容器。如果子元素定位异常,优先看父级有没有position: relative;如果宽度无法撑开,看父级的display是不是 flex 或者 grid,是否触发了子项的flex-shrink或min-width限制。
第四步,检查是否有全局样式干扰。比如 reset 样式、第三方组件库的统一样式,它们可能在你不知情的情况下修改了box-sizing或line-height。
这套流程在面试时可以放在 30 秒内说完,但每一句背后都是真实的排查经验。面试官听完基本能确定你是有实战的人。
5.3 给面试者的最后建议:代码规范、工程化思维与经验增量
CSS 面试准备的最后一个层面是“工程化和规范”。很多人忽略了这一点,但其实面试官最后往往通过“你怎么组织一个大型项目的样式”来判断你这个人的成熟度。
我的建议是至少准备三段话:第一段讲命名规范,比如 BEM(Block Element Modifier)或语义化类名;第二段讲分文件策略,比如 reset、变量、组件样式、页面样式如何拆分;第三段讲构建层面的处理,比如 CSS Modules、PostCSS、自动加前缀、压缩合并。
如果你还能提到“设计系统”和“设计变量”——比如把颜色、间距、圆角、阴影抽成 CSS 变量,浅色深色主题一键切换——那就更好了。这代表了你有从设计侧反推前端实现的经验,而不仅仅是会写选择器。
6. 写在最后:我把 CSS 面试当成一次梳理机会
我遇到过很多人对 CSS 面试焦虑,觉得自己“会写页面但讲不出原理”。其实有这种感受很正常,因为 CSS 和 JS 的学习路径完全不一样——你不会写 JS 就无法运行,但不会写 CSS 也能靠浏览器的默认样式硬把页面撑出来,这就导致很多人写了几年页面,底层原理却一直模糊。
根据我个人准备和面试别人的经验,最有效的方式还是务实一点:每遇到一个搞不清楚的样式现象,就开一个本地 demo 做实验,观察不同属性值带来的渲染差异,再顺手写进你的错题本。你不需要成为一个 CSS 背诵机器,你只需要对“为什么会出现这个现象”保持足够的好奇心。面试题本身只是敲门砖,真正让面试官记住你的,是你解释问题时的从容和对细节的敏感度。
最后再分享一个小技巧:面试前把最常用的 20 个 CSS 属性过一遍,不看文档,自己在纸上写出它们的完整写法、默认值、以及至少一个坑。写不出来的、写错的,就是你要重点补的地方。这个办法我试过很多次,几次面试里被问到的考点,恰好就是这个查漏补缺的过程里刚补上的点。