做前端这几年,我见过太多新人一上来就啃框架、背面试题,结果连一个最基础的静态页面都写不利索。HTML和CSS从来不是"没人学的老古董",而是整个前端行业的生存底线。标题里那句"HTML搭好台,CSS属性大全来救场"说得特别准——HTML是把页面结构搭出来的那双手,CSS则是决定页面最终长什么样的那层妆。这篇东西不聊框架,不扯构建工具,就实打实地把HTML和CSS这两样"生存口粮"讲透,给正在入门前端或者刚起步没多久的萌新一份能直接照着用的手册。
1. HTML骨架搭建的底层逻辑与核心规范
1.1 文档结构:那行DOCTYPE和html标签到底在干什么
任何一个合格的HTML页面,第一行都应该是<!DOCTYPE html>。很多萌新刚打开编辑器敲代码时,嫌这行字碍眼,直接删掉,或者随手复制一个不规范的版本。这里我得说句重话:这行东西不是摆设,它告诉浏览器"我用的是HTML5标准语法来解析这个页面",没有它,浏览器会进入一种叫"怪异模式"的解析状态,后果就是你写的CSS可能会出现各种莫名其妙的偏差——比如盒模型算不对、元素默认间距对不上。
紧接着的<html lang="zh-cn">同样有讲究。lang属性不只是给搜索引擎看的,它会告诉浏览器这个页面的默认语言是什么,影响屏幕阅读器的发音、浏览器的翻译建议、还有字体渲染的偏好。做中文站点就老老实实写lang="zh-cn",别照抄英文站点的lang="en"。
<head>里面比较核心的几个标签,我按重要性排个序:
<meta charset="utf-8">:不写这个,页面上所有中文都有可能变成乱码,这是最致命的问题。<meta name="viewport" content="width=device-width, initial-scale=1.0">:不写这个,手机浏览器打开页面会先按980px宽度的桌面布局渲染,然后整体缩小,文字小到根本没法看。<title>:页面标题,影响浏览器标签页显示和搜索引擎收录。
head里还有一个容易被忽略的点:<meta name="description">和<meta name="keywords">虽然对搜索权重的影响已经大不如前,但description在搜索结果里展示的摘要文本依然来自它,写得好能明显提升点击率。这个属于SEO的范畴,萌新阶段了解即可,但把骨架搭标准了,后面少走很多弯路。
1.2 语义化标签:别把div用成万能膏药
我刚带团队的时候,review新人代码最头疼的就是一水儿的<div>和<span>。不是说div不能用,而是你通篇只用div,等于在告诉浏览器和搜索引擎"我不知道我页面里哪个是导航、哪个是底部、哪个是正文"。
HTML5给了一整套语义化标签,我的建议是至少把这几个记牢:
<header>:页面头部或区块头部<nav>:导航区域<main>:页面主体内容(一个页面建议只用一次)<section>:有主题的区块<article>:独立内容,比如一篇博客、一条评论<aside>:侧边栏或与主体关系不大的补充内容<footer>:底部信息区
这背后有一个非常现实的好处:可访问性。屏幕阅读器用户是靠标签来"听"页面结构的,你把导航写成<div class="nav">,读屏软件就是一个普通文本流,视障用户根本没法快速跳过导航直接到正文。而写成<nav>,读屏软件会识别并提示"导航区域,共5个链接",这个体验差距是巨大的。况且语义化标签对SEO也友好,搜索引擎抓取页面时能更准确判断内容结构,这是个投入产出比极高的事情。
1.3 标签嵌套与层级:一层层安排明白
HTML的嵌套规则说简单也简单,说坑也坑。简单的是:绝大多数块级标签可以互相嵌套,但有几个例外,我踩过也看别人踩过:
<p>标签内不能嵌套块级元素,比如<div>。浏览器解析时会把</p>提前闭合,导致结构错乱。<a>标签可以嵌套块级元素(HTML5允许),但不能嵌套另一个<a>,否则浏览器会自动断开。<ul>和<ol>的直接子级只能是<li>,中间不能夹<div>。
这些规范看着细碎,但前端开发的第一步就是把结构写对。结构不对,后面CSS写得再好都是空中楼阁。另外多说一句:缩进和注释一定要养成习惯。我见过不少萌新写代码,标签一层套三层,全部贴着左边缘对齐,别说别人了,自己隔三天回来看都头晕。代码首先是给人看的,其次才是给机器跑的。
2. CSS属性大全:从盒模型到布局的主干体系
2.1 盒模型:前端萌新的第一道分水岭
如果说HTML是骨架,那CSS盒模型就是理解CSS一切布局的基础。所谓盒模型,就是把页面上的每个元素都看作一个盒子,这个盒子由四部分组成:content(内容区)、padding(内边距)、border(边框)、margin(外边距)。
宽度和高度的计算方式,取决于box-sizing属性的设置:
content-box(默认):width只包含content,padding和border要额外加上去。比如你设width: 300px,又加了padding: 20px和border: 1px,实际占位是300 + 20×2 + 1×2 = 342px。border-box:width包含content、padding和border,三者内部自动挤压。设了width: 300px,无论怎么改padding,总宽度都是300px。
我把所有新项目的全局样式第一行都写成:
* { box-sizing: border-box; }这样布局时的尺寸计算非常直观,不给"实际宽度超出预期"这种问题留机会。有一点需要提醒:margin永远不会被width包含,margin是两个盒子之间的间距,它是盒子外部的东西,跟box-sizing无关。很多萌新在调试间距时把margin理解成padding,结果怎么调都不对,其实就是没分清这个边界。
2.2 布局属性:Flexbox和Grid的实战心得
当年我用float布局做页面的时候,每次清浮动都要写clearfix,烦得很。现在Flexbox和Grid已经全面普及,萌新可以直接跳过float布局,直接把这两个家伙学扎实。
Flexbox适合处理一维布局(一行或一列),核心就几个属性:
display: flex:开起来flex-direction:方向,row(行)还是column(列)justify-content:主轴对齐,center、space-between、space-aroundalign-items:交叉轴对齐,center、flex-start、stretchflex-wrap:是否换行
一个非常典型的应用:让子元素在父容器里水平垂直居中。以前要写一堆margin或者定位,现在三行代码:
.parent { display: flex; justify-content: center; align-items: center; }Grid适合处理二维布局(同时管行和列)。我现在做页面整体的框架,比如某一块区域要分成"左侧边栏240px + 右侧自适应",我首选Grid:
.layout { display: grid; grid-template-columns: 240px 1fr; grid-template-rows: auto 1fr auto; gap: 16px; }这里的核心逻辑是:把页面想象成一张网格纸,网格线的划分规则写在grid-template-columns和grid-template-rows里,子元素按区域去"占格子"。1fr代表"剩余空间的一份",这是响应式布局的利器。
顺便说一个细节:gap既能用在Grid也能用在Flexbox的换行场景,它专门管"子元素之间的间距",用了它就不用再给子元素单独设margin了,省心。
2.3 文本与字体:让人读着舒服的基础属性
前端页面终极目标是给人看的,文本排版的体验直接决定用户是否愿意在你的页面上停留。文本相关的CSS属性里,我挑几个关键的说:
font-size:字号,建议用px或rem,关于rem后面讲line-height:行高,我习惯设置成1.5到1.7之间,太紧的文本读着累,太松了又显得散letter-spacing:字间距,中文正文不建议设太夸张,0.02em到0.05em即可text-align:对齐方式,中文正文一般左对齐,justify两端对齐虽然整齐但有时会出现字间距不均text-overflow: ellipsis:文本超出省略号,需要配合white-space: nowrap和overflow: hidden一起用
字体族font-family有一个常规套路:中文字体排第一位,英文字体其次,最后加兜底字体。我常用的写法:
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; }这样在苹果设备上使用苹方,Windows上使用微软雅黑,系统有谁就用谁,加载快还不失真。
2.4 背景与边框:视觉表达的基础手段
背景属性在开发中几乎每天都用。background是一个合写属性,可以一次性设置颜色、图片、位置、尺寸等:
.banner { background: #f5f5f5 url("./images/banner.jpg") no-repeat center center / cover; }上面对应的是:背景色#f5f5f5,背景图路径,不重复,水平垂直居中,缩放模式cover(覆盖整个容器,图片可能被裁切,但不会变形拉伸)。
这里的两个取值大家要理解清楚:cover是让图片完全覆盖容器,图片可能被裁掉边缘;contain是让图片完整显示在容器内,容器可能露出背景色。萌新经常把这两个搞混,做Banner图时选cover基本没错,做产品图的时候选contain更合适。
边框border相对简单:border: 1px solid #ddd三要素分别是粗细、线型、颜色。但有一个小细节:给卡片加边框时,我更喜欢用box-shadow来模拟边框(比如0 0 0 1px #ddd),因为box-shadow不占盒模型的尺寸,不会因为边框导致元素间间距波动。这个技巧在hover描边效果里特别常用,不会让卡片在鼠标悬停时"跳一下"。
3. CSS进阶属性:动效、响应式与变量
3.1 过渡动画:transition的触发与参数选择
transition是CSS动效里最简单的成员,它的原理是:当属性值发生变化时,不瞬间跳变,而是在一段时间内平滑过渡。我把它理解成"属性值的插值器"。
基本写法:
.btn { background: #4a90d9; transition: background-color 0.3s ease; } .btn:hover { background: #357abd; }鼠标悬停时,背景色会在0.3秒内从浅蓝过渡到深蓝。transition的四个参数:要过渡的属性、时长、缓动函数、延迟时间。ease是默认缓动,先快后慢,看起来自然;linear是匀速,适合颜色变化;cubic-bezier可以自定义缓动曲线,调得好能做出弹跳感。
这里有几个萌新常踩的坑:
- 给所有属性都加transition,比如
transition: all 0.3s,导致hover时莫名其妙的东西都跟着动,性能还掉。 - 过渡的属性与值之间的"可插值性"要考虑,比如
display是离散属性,无法过渡,应该用opacity加visibility来配合。
正确的做法是:只对需要平滑变化的属性(如opacity、transform、background-color)写transition,千万别无脑all。
3.2 关键帧动画:@keyframes的运用场景
transition需要"触发条件"(比如hover),而@keyframes动画是自动播放的,适合做加载、提示、入场这些效果。基本结构是定义关键帧,然后通过animation属性调用:
@keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } .card { animation: fadeInUp 0.6s ease-out both; }from和to分别代表起始帧和结束帧,还可以用百分比定义中间帧,比如50%的时候做一次弹跳。animation后面的简写顺序一般是:动画名、时长、缓动函数、延迟时间、播放次数(infinite表示无限循环)、alternate表示往返播放。
最后那个both其实很少有人解释清楚。它对应animation-fill-mode属性,表示动画结束后保持最后一帧的样子(forwards),动画开始前先应用第一帧(backwards),both就是两个都要。如果不加fill-mode,动画播放完元素会直接跳回初始状态,那种"闪一下"的观感就是这个问题造成的。
3.3 响应式布局:媒体查询的断点策略
现在的网站几乎都要在手机和电脑上同时看,响应式是前端绕不开的核心。媒体查询的核心语法:
@media (max-width: 768px) { /* 屏幕宽度小于等于768px时应用的样式 */ }断点怎么选?我个人的策略是一般就设两个断点:768px(手机与平板的分界)和1024px(平板与桌面的分界)。这套断点不是拍脑袋定的,它对应了常见设备的尺寸分布。设置断点的核心思路是"移动优先"还是"桌面优先"。移动优先的话,默认样式按手机写,再用min-width逐步增强;桌面优先就是默认写桌面样式,用max-width逐步覆盖。
关于实现方式,我建议在写布局时优先用灵活的容器,比如flex、grid、百分比和rem,让页面在大多数宽度下都自然"流动",媒体查询只用来处理明显需要改变布局形态的位置。
顺带提一个很实用的技巧:用clamp()函数替代一些需要静态断点的地方。比如字号可以在一定范围内自适应:
h1 { font-size: clamp(1.5rem, 2vw + 1rem, 3rem); }这样标题在小屏和大屏之间平滑变化,不需要写媒体查询。clamp(最小值, 弹性值, 最大值)三个参数,我把中间的2vw + 1rem称为"按视口宽度变化的弹性值",你可以自由调整。
3.4 CSS变量与calc():让样式可维护的设计决策
CSS变量(custom properties)是我非常推荐萌新尽早使用的特性,它解决的是"样式常量复用"的问题。在:root里定义全局变量,然后任何地方都能引用:
:root { --primary: #4a90d9; --spacing-base: 16px; --radius-base: 8px; } .btn { background: var(--primary); border-radius: var(--radius-base); padding: var(--spacing-base); }这样做的好处,我想做一次改版的朋友深有体会:以前改一个品牌色,满项目搜索#4a90d9,替换时还怕漏了深浅不一致的版本;有了CSS变量,改:root那一个值,全站瞬间变脸。变量还能跟JavaScript配合,比如:
document.documentElement.style.setProperty("--primary", "#333");动态切换主题就是这么简单。
calc()则是让CSS可以做简单的计算。最经典的场景:元素宽度占父容器100%减去一个固定间距:
.item { width: calc(100% - 240px); }注意运算符两边必须有空格,写calc(100%-240px)是无效的。这个语法细节很隐蔽,第一次遇到报错的人会一脸懵。
4. 实操复现:从零搭好一张带导航的页面
4.1 先搭HTML骨架,再谈样式
理论说了那么多,不如实际走一遍。下面我要做一个完整的页面,结构上是一个"顶部导航 + 主体卡片区 + 底部信息"的典型页面。先把HTML骨架写出来:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>前端萌新练习页</title> <link rel="stylesheet" href="./style.css"> </head> <body> <header class="site-header"> <div class="container"> <div class="logo">MixLab</div> <nav class="nav"> <a href="#home">首页</a> <a href="#articles">文章</a> <a href="#about">关于</a> </nav> </div> </header> <main class="container main-layout"> <section class="card intro" id="home"> <h1>HTML搭好台,CSS属性来救场</h1> <p>一个简单的响应式页面示例,你可以直接打开浏览器看效果。</p> </section> <section class="card article-list" id="articles"> <h2>最近更新</h2> <ul> <li>盒模型理解指南</li> <li>Flex布局实战笔记</li> <li>CSS变量整理手册</li> </ul> </section> </main> <footer class="site-footer"> <div class="container"> <p>手写前端,打好地基。</p> </div> </footer> </body> </html>这个结构里我用了header、main、section、footer这些语义化标签,这样就算没有一行CSS,浏览器和读屏软件也能理解页面的大致分区。页面整体分成顶部导航、中部内容、底部版权三个大区块,内容区里再分成两个卡片。
4.2 CSS布局与样式:逐行讲清设计意图
现在给这个页面写CSS。我的整体思路是:先做全局重置,再定页面框架布局,最后落卡片和文本的细节。
/* 全局重置 */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif; line-height: 1.6; background: #f5f6f8; color: #333; } .container { width: min(960px, 92%); margin: 0 auto; } /* 顶部导航 */ .site-header { background: #fff; border-bottom: 1px solid #eee; position: sticky; top: 0; z-index: 10; } .site-header .container { display: flex; justify-content: space-between; align-items: center; height: 60px; } .logo { font-size: 24px; font-weight: 700; color: #2c3e50; } .nav a { text-decoration: none; color: #555; margin-left: 20px; transition: color 0.2s ease; } .nav a:hover { color: #3498db; }这段有几个值得展开的点:
第一,width: min(960px, 92%)这个写法,意思是"取960px和视口宽度92%两者中更小的那个"。页面在宽屏上最多960px,窄屏上自动缩到视口92%。这比写死width: 960px加max-width: 92%更简洁,也省了一条媒体查询。
第二,position: sticky给导航加了吸顶效果。sticky是个有意思的定位方式,它在正常文档流里占位置,但滚动到超过它top: 0的位置时,会"粘"在视口顶部。但要注意,sticky的生效条件是它的父元素高度大于它自身,如果父元素只有导航栏这么高,它根本无处"粘"。
第三,hover我用了transition: color 0.2s ease,只给color做过渡,这是刻意控制动效成本,不让整个导航在hover时触发重排。
接着写主体布局和卡片:
.main-layout { display: grid; grid-template-columns: 1fr; gap: 24px; padding: 32px 0; } .card { background: #fff; border-radius: 12px; padding: 24px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); } .intro h1 { font-size: clamp(1.8rem, 3vw + 0.5rem, 2.6rem); color: #2c3e50; margin-bottom: 12px; } .intro p { color: #777; } .article-list ul { list-style: none; } .article-list li { padding: 12px 0; border-bottom: 1px solid #f0f0f0; } .article-list li:last-child { border-bottom: none; } .site-footer { background: #fff; border-top: 1px solid #eee; padding: 24px 0; text-align: center; color: #999; font-size: 14px; }卡片我这里用的是纵向单列布局,卡片之间用gap: 24px隔开,阴影用了比较柔和的box-shadow而不是硬边框,整体更干净。标题字号用clamp()实现响应式,这一点前面讲过原理。
4.3 加上平板与手机适配的两条媒体查询
基础样式写完后,要让它适应手机端。我加了两条媒体查询:
@media (min-width: 768px) { .main-layout { grid-template-columns: 1fr 1fr; } } @media (max-width: 600px) { .site-header .container { flex-direction: column; height: auto; padding: 12px 0; gap: 8px; } .nav a { margin-left: 12px; margin-right: 12px; } .card { padding: 16px; } }这里采用的策略是"桌面基础 + 平板增强 + 手机覆盖"。默认是单列,到平板宽度就变成两列并排,到了手机宽度就改成竖直排列导航改成横排小字号。你可能会问:为什么不直接全部用flex自动换行?其实也行,但对萌新来说,清晰地控制断点变化反而更容易理解每个尺寸下页面具体长什么样。等以后熟练了,再慢慢从flex/grid自适应和clamp等"液体布局"手段中解放出来。
5. 常见问题排查:样式不生效的九个真实案例
5.1 选择器优先级:为什么我写的样式被覆盖了
CSS优先级是一个必须烂熟于心的规则:!important> 内联样式 > ID选择器 > 类选择器、属性选择器、伪类 > 标签选择器、伪元素。同级情况下,后写的覆盖先写的。
最常见的坑:写了一个.box { color: red; },页面里看到的却是蓝色,大概率是因为某个标签选择器或更靠后的同名类把颜色覆盖了。我排查的固定步骤是:
- 浏览器按F12打开开发者工具,选中那个元素。
- 看右侧Styles面板,从上到下列出了所有命中该元素的CSS规则。
- 被划掉的那条规则说明被覆盖了,找到生效的那条,检查它的选择器和来源。
这个操作步骤,我建议每个萌新都养成习惯。排查样式问题,第一动作永远是"打开控制台看元素",而不是在代码里瞎改乱试。
5.2 margin塌陷:父子元素间距怎么总不对
给子元素设置margin-top,结果移到父元素身上去了,父元素跟着一起往下挪。这是经典的margin塌陷问题,根因是CSS规范里对"相邻的垂直margin会合并"的规定。
处理方案有三个:
- 给父元素加
padding-top代替margin-top。 - 给父元素加
overflow: hidden,创建一个BFC(块级格式化上下文),让内部margin不往外溢出。 - 给父元素加
display: flex或display: grid,形成独立上下文。
我实际项目里最常用的就是第三种和第一种:布局本身就需要flex/grid,顺手就解决了。如果是普通块级容器,就改用padding。
5.3 图片底部为什么多了一条白边
这个现象看起来玄乎,其实是图片是行内元素,行内元素默认按文字基线(baseline)对齐,而基线的下方还留有"下沉空间"。解决方式是给图片设置display: block,或者用vertical-align: bottom让图片底部对齐到父容器底部边缘。一般在书写CSS时,我会直接对全局img设置display: block来提前规避:
img { display: block; max-width: 100%; }5.4 flex子元素被压缩导致宽度不对
在flex容器里,子项的默认行为是flex-shrink: 1,空间不够时自动压缩。如果设置了width: 300px,但父容器只剩250px,子项会被压缩到250px。萌新经常因此困惑。解决方案是设置flex-shrink: 0,或者用flex: 0 0 300px这个更完整的写法定死不伸缩。同理,flex: 1就等于flex-grow: 1; flex-shrink: 1; flex-basis: 0%,是"填满剩余空间"的意思,这两种写法要能分清。
我把这些高频问题汇总成一张速查表,方便你直接对照:
| 现象 | 可能原因 | 排查动作 | 常用解法 |
|---|---|---|---|
| 样式没生效 | 选择器优先级不够、代码位置靠前 | DevTools查看命中规则 | 提升优先级或调整声明位置 |
| 父子margin重叠 | margin塌陷 | 观察父元素是否跟着移动 | padding替代、flex容器或overflow |
| 图片底部有白边 | 行内元素基线对齐 | 检查img元素默认display | img { display: block; } |
| flex子项宽度不准 | 子项默认可压缩 | 看子项是否被压缩 | flex-shrink: 0或flex: 0 0 宽度 |
| 布局被视口拉伸 | 未设viewport meta | 手机打开看文字大小 | 补<meta name="viewport"> |
| 网页出现横向滚动条 | 某些元素超出视口 | 检查body宽度和overflow | 排查子元素宽度,考虑overflow-x: hidden |
5.5 兼容性:同一个属性在不同浏览器显示不同
虽然现在的浏览器环境比几年前好多了,但某些属性的兼容差异依然存在。我的原则是:动手查阅一个网站叫Can I Use(caniuse.com),输入属性名就能看到各浏览器的支持情况。如果你要用gap在纯flex布局里,如果团队还要求兼容较老浏览器,就要考虑用margin来等价实现。CSS NaN似的知识点很多,建立"用之前查兼容性"的习惯,能少踩很多暗坑。
6. 前端萌新把技术变饭碗的进阶路线
6.1 学会"重写"是最好的学习方法
我给新人定的第一个实战任务不是做新的页面,而是找一个现成网站,比如一个自己常用的数据管理后台页面,把它用HTML和CSS一比一重写一遍。这个过程会逼着你研究它的布局逻辑、间距系统、字体选择、hover效果,学到的东西比看一百篇教程都有用。
重写之后,再尝试改造:把固定宽度改成响应式,把硬编码颜色抽成CSS变量,把hover效果补上过渡动画。这样一轮下来,HTML和CSS的大多数重要属性都可以覆盖到。我自己当年就是这么练的,把一个老版企业站重写了两遍,第二次明显比第一次流畅多了。
6.2 面试时HTML和CSS考什么
结合面试题的常见方向,我给萌新列几条必须答得上来的:
- 盒模型是什么,content-box与border-box有什么区别
- 四类定位(static、relative、absolute、fixed)的参考点分别是什么
- flex和grid适用场景分别是什么
- 如何用CSS实现水平垂直居中(至少说出两种方案)
- 如何提升页面加载性能:CSS文件放head,JS文件放body底部,图片懒加载等
px、em、rem、vw的区别和选择
这些不是背诵题,是实操题。面试官通常会让你现场写一个布局,或者针对你的页面代码追问某个属性的表现。平时动手练得多,面试基本裸考也能过得去。
我在实际带人的过程中最深的一个体会是:HTML和CSS这种"基础中的基础",恰恰是天花板很高的地方。你把盒模型和flex搞透了,后面学CSS框架、UI组件库都是水到渠成的事;反之如果基础不牢,哪怕背熟了组件库API,一出问题还是两眼一抹黑。
最后再分享一个小技巧:CSS属性不用刻意去背,按照"布局→排版→背景边框→动画→响应式"这条线,每个方向做两三个小练习,属性就会自然长在脑子里。我见过很多同学收藏了一大堆"CSS属性大全""速查手册",真正用的时候还是去翻浏览器控制台,这没什么不好意思的——前端这行,会查会调试,远比死记硬背更能活下去。