主题
整体准备与topbar(Day20 1/3)
本页对应 Day20_PC项目,共 6 段视频,约 1 小时 11 分。
学习目标
- 能够按“入口文件 + 变量文件 + 混合文件 + 重置样式”拆分 Less,并通过
@import在index.less中汇总,最终在 HTML 中只引入编译后的index.css。 - 能够使用 Flex 布局完成 topbar 左右分区与两端对齐,并通过链接右边框和
:not(:last-child)排除每组最后一个链接,实现导航竖线分隔。 - 能够完成 logo 与搜索框区域布局:设置 logo 宽高和外边距,设置 input/button 的宽高、内边距、边框、
box-sizing、背景色与文字颜色,并用 Flex 消除行内元素换行空白。 - 能够使用版心容器和 Flex 完成页面导航:设置最小宽度避免视口小于 1200px 时背景缺失,完成“全部分类”区域及导航项的宽度、高度、间距、字号、颜色和高度拉伸。
- 能够实现路径导航(面包屑):用
span包裹链接,通过嵌套选择器与::after伪元素添加斜杠分隔,排除最后一个span,并用 Flex 去除span间换行空白。 - 能够搭建放大镜区域布局:使用相对/绝对定位定位大图与遮罩,使用
overflow: hidden裁切大图,设置 400×400 容器与图片尺寸、边框,并完成半透明遮罩的尺寸、背景、边框及默认隐藏。
知识点
整体准备:先拆文件,再写页面
为什么需要:PC项目内容多,如果一口气写到底,回头找某一块会非常困难。转写稿里讲师反复强调“每完成一部分保存成一个文件”,目的就是方便定位和复用。
怎么想:先不急着写样式,先把目录和入口理清:index.html 管结构,index.less 作为总入口,变量、混合、重置样式分别独立。
怎么做:建“00-整体准备”,准备 index.html;CSS 侧建 index.less、变量文件、混合文件;把 reset.css 改成 reset.less,再在入口中导入。转写稿明确出现的导入逻辑是:
less
@import "variables";
@import "mixins";
@import "reset";变量按口述校正为 form-red、btn-red、sep-color 等,分别对应表单红、按钮红、分割线颜色。混合先定义清除浮动,内部用 &:after。
易错点:@import 一个 .css 文件会把导入语句原样编译进 CSS,所以要把 reset.css 改成 reset.less;变量、混合文件不单独编译,只编译 index.less;编译后的 CSS 可能没有注释,调试要看 index.less 和生成的 index.css,不要误判“没编译”。
topbar:背景横跨,内容进版心
为什么需要:顶部栏背景要横跨整页,但文字内容不能贴边,所以最外层负责背景,里层 container 负责 1200 宽版心。
怎么想:结构上 topbar 包 container,再分左右:左边欢迎词、登录、免费注册,右边导航链接。竖线不额外做元素,直接给 a 加右边框。
怎么做:topbar 设背景色、高度 30、行高;container 设宽度 1200、左右 margin: auto,并 display:flex; justify-content:space-between。所有 a 加左右内边距 10,加右边框 1px solid @sep-color,再用 &:not(:last-child) 排除各自最后一个。
易错点:版心不能直接承担整页背景;左右两组各自的最后一个都要排除;Less 嵌套编译后会变成 a 与 a:not(:last-child) 的组合选择器,看不懂时对照生成的 CSS。
logo 与搜索框:固定尺寸 + 伸缩盒消空白
为什么需要:logo 是品牌入口,搜索框是核心操作区,二者要在一行内两端对齐。
怎么想:外层 container 用 flex 两端对齐;logo 是 a,在 flex 项目里可以设宽高;表单内部放 input 和 button,用 flex 消掉换行产生的空白。
怎么做:logo 宽 175、高 56,外边距上下 25、左右 45;input 设 box-sizing:border-box、宽 490、高 32、内边距上下 0 左右 4、边框 2、颜色 @form-red,并去掉 outline;按钮默认就是 border-box,设宽 68、高 32、背景 @btn-red、文字白色、边框 none;表单整体 margin-top:35,并 display:flex。
易错点:input 不设 border-box 会把边框和内边距算到总宽外;按钮默认 border-box 不要重复设;outline:none 后要保证视觉可辨识;input 与 button 之间的空白用 flex 解决最稳。
页面导航:高度交给容器,子项自然拉伸
为什么需要:商品分类和页面导航需要横向排列,并且整体有下边框和固定高度。
怎么想:外层 product-nav 加下边框;container 变成 flex 并给高度 50,让分类块和导航在侧轴上拉伸到同高;分类块单独设背景、宽 210、文字居中。
怎么做:product-nav 设 border-bottom:2px solid @btn-red;container 设 height:50; display:flex;分类块背景 @btn-red、宽 210、文字白色、字号 14、行高 50;右侧 nav 设 flex,内部 a 设外边距上下 0 左右 22、颜色 #333、字号 16。
易错点:高度给 container 后,flex 子项默认拉伸;nav 的层级不要写错,否则选择器不生效;行高可统一在容器上让后代继承。
路径导航:面包屑与伪元素斜杠
为什么需要:路径导航告诉用户当前页面在网站分类中的位置,也叫面包屑导航。
怎么想:外层 main 包 container,路径导航整体设内边距;每个分类用 span 包住 a,斜杠用 a 或 span 的 ::after 生成,这样只有文字区域是超链接。
怎么做:main 设上外边距 15;路径导航设整体内边距;span 的 ::after 用 content:"/" 或 Unicode 编码,颜色 #ccc,内边距上下 0 左右 5;用 &:not(:last-child)::after 排除最后一个;再把 span 变成 flex 项目消除换行空白。
易错点:直接给 a 加伪元素会让空白区域也可点;排除最后一个时选择器较长,按“父级下的 span,排除最后一个,再加伪元素”顺序读;斜杠用 content 写 / 或编码都可,转写稿提到用编码显示斜线。
放大镜区域:先布局,再定位
为什么需要:商品预览需要左图右参数,左侧还要分大图、小图和半透明蒙层。
怎么想:product 用 flex 两端对齐,左边 product-preview 分 zoom 和缩略图;zoom-box 做相对定位包含块,小图和大图都 400×400,大图绝对定位到右侧,蒙层只放在小图上并默认隐藏。
怎么做:product 设上外边距 5、下 15、flex、space-between;small-image、large-image 设 400×400、边框 #DFDFDF;大图设 overflow:hidden,position:absolute; left:410; top:0;蒙层 mask-box 设 200×200、半透明白、边框 #DDD、绝对定位,先 display:none。
易错点:大图不设 overflow:hidden 会溢出;绝对定位必须检查包含块是否相对定位;蒙层要写在小图结构下;资源路径按转写稿保留为 logo.png、S3 小图/大图等,不要随意改文件名。调试时先看结构有没有出来,再引入 index.css,最后逐项补样式。
整体准备:先拆文件,再写页面
为什么需要:PC项目页面长、样式多,如果所有LESS都堆在一个文件里,写到后面很难查找。转写稿里反复强调“每完成一部分保存成一个文件”,就是为了一部分一部分推进。
怎么想:把“变量、混合、重置样式、入口”拆开,HTML只引入编译后的index.css。reset.css虽然原来是CSS,但可以直接改成reset.less,因为LESS里可以写CSS。
怎么做:
less
// index.less
@import "variables";
@import "mixins";
@import "reset";
.container {
width: 1200px;
margin: 0 auto;
}变量文件里先定义表单红、按钮红、分割线颜色;混合文件里定义清除浮动。注意只编译index.less,其他文件通过@import导入。
易错点:不要编译variables.less、mixins.less;HTML只引index.css,不要再单独引reset.css。转写稿里“编译没反应”其实是看错保存结果,实际编译结果里已经带入了reset内容。
topbar骨架:外层横跨,版心居中
为什么需要:topbar背景色和下边框要横跨整个页面,但内容宽度只有1200px,所以不能把背景直接加在版心上。
怎么想:外层.topbar负责背景、高度、行高;里面放.container负责1200px版心;版心内再分左右两块,用伸缩盒两端对齐。
怎么做:
html
<div class="topbar">
<div class="container">
<div class="topbar-left">
<span>欢迎来到商品会</span>
<a href="#">请登录</a>
<a href="#">免费注册</a>
</div>
<div class="topbar-right">
<a href="#">我的订单</a>
<a href="#">我的购物车</a>
</div>
</div>
</div>less
.topbar {
height: 30px;
line-height: 30px;
min-width: 1200px;
display: flex;
justify-content: space-between;
.topbar-left,
.topbar-right {
a {
padding: 0 10px;
border-right: 1px solid @sep-color;
&:last-child {
border-right: none;
}
}
}
}易错点:窗口小于1200px时,topbar背景会跟着变窄,版心溢出部分没有背景,所以外层要加min-width: 1200px。另外&:last-child要写在a里面,不要写到.topbar外面。
右侧导航竖线:用A的右边框,排除最后一个
为什么需要:转写稿里这次不额外用元素做竖线,而是直接给A加右边框,结构更简单。
怎么想:左右两组A各自最后一个都不需要右边框,所以不能只排除整个topbar的最后一个A,要分别处理.topbar-left a:last-child和.topbar-right a:last-child。
怎么做:给A统一加padding: 0 10px和border-right: 1px solid @sep-color,再用&:last-child { border-right: none; }排除各自最后一个。
易错点:如果只写.topbar a:last-child,只会排除整个topbar最后一个A,左侧最后一个仍会有竖线。嵌套写法生成的选择器要能看懂:.topbar-left a:last-child。
logo与搜索框:flex解决对齐
为什么需要:logo和表单要左右分布,input和button默认会因换行产生空白,直接并排容易对不齐。
怎么想:外层.header用display:flex; justify-content:space-between;表单form也变成伸缩容器,input和button就紧贴对齐。
怎么做:logo图片按资料设宽高和上下25、左右45的外边距;input设box-sizing: border-box、宽高、内边距、边框;button默认就是border-box,设宽高、背景、白字,并去掉outline。
易错点:不要忘记box-sizing: border-box,否则input总宽会超出;button默认边框要border:none;表单整体加上外边距35px。
页面导航与路径导航:高度拉伸与伪元素斜线
为什么需要:页面导航要让“全部分类”和右侧菜单同高;路径导航要用斜杠分隔,但斜杠不能写死成图片。
怎么想:页面导航的.container设height:50px; display:flex,子项默认拉伸,所以左右都高50;分类设背景、白字、居中、宽度。路径导航用span包住A,给span::after写斜杠,最后一个span排除。
怎么做:伪元素里写content: "/"或对应编码,设颜色和左右间距;span设为伸缩项目,消除换行空白。选择器类似.path span:not(:last-child)::after。
易错点:伪元素要加在span上而不是A上,否则空白区域也有小手;最后一个span要排除;换行空白用display:flex消掉。
放大镜区域:父相子绝与溢出隐藏
为什么需要:大图要定位到小图右侧,蒙层要覆盖在小图上,图片超出400×400区域必须隐藏。
怎么想:.zoom-box设相对定位,作为包含块;大图.large-image设绝对定位,left:410px; top:0;小图区域overflow:hidden;蒙层.mask-box绝对定位,默认隐藏,鼠标移入再显示。
易错点:父元素忘了position:relative,绝对定位会跑偏;大图不设overflow:hidden会溢出;蒙层宽高200×200时若加边框,要用box-sizing:border-box,否则总尺寸变大。
本节要完成什么
这一节做商品页头部的 logo 和搜索框。转写里把这一块读作“配置杠head”,下面用 header 代称,实际以你项目里的类名为准。为什么需要单独做这一块?因为顶部导航栏已经完成,接下来就是页面头部:左边放 logo,右边放搜索表单。它既要和上面的 topbar 保持同样的版心对齐,又要为后面的页面导航、商品区域打基础。怎么想?先按“外层通栏 + 版心 + 左右两栏”搭结构,再按资料给的尺寸写样式。怎么做?继续在 index.less 里写,最后只编译出 index.css,HTML 只引入编译后的 CSS。
先搭 HTML:左边 logo,右边表单
结构不要一上来就堆类名。转写里的思路是:这一块外层给一个头部区域,里面套版心,版心再分左右。左侧 logo 用 a 包 img,右侧用 form 包 input 和 button。素材继续用资料里的 logo.png,图片 alt 写成“网站 logo”。结构示意如下:
html
<div class="header">
<div class="container">
<a href="#" class="logo">
<img src="./images/logo.png" alt="网站 logo">
</a>
<form class="search-form">
<input type="text" class="search-input">
<button class="search-btn">搜索</button>
</form>
</div>
</div>为什么 logo 外面要用 a?因为实际页面里 logo 通常可点击回首页。怎么想?版心 .container 负责 1200px 宽度和左右自动外边距;.header 只负责整块区域。易错点:不要忘了在 HTML 里引入 index.css,不是引入 index.less;reset 已经通过 index.less 导入,不要再单独引入一遍。
样式:版心两端对齐,logo 按尺寸留白
转写里明确要求:logo 宽度 175、高度 56,外边距上下 25、左右 45;版心要让左右两部分分开。做法是给 .container 加 Flex:
less
.header {
.container {
display: flex;
justify-content: space-between;
}
.logo {
width: 175px;
height: 56px;
margin: 25px 45px;
}
}为什么用 justify-content: space-between?因为左边 logo、右边表单,正好一左一右。怎么想?.logo 虽然是 a,默认是行内元素,但它现在是 Flex 项目,可以设置宽高。易错点:只给 a 设宽高时,要检查图片是否撑满或溢出;如果图片尺寸不合适,再按资料检查 img 的显示。刷新后如果 logo 位置不对,先看外边距有没有写错方向,再看版心是否真的变成了 Flex 容器。
搜索框:input 与 button 的尺寸和边框
右侧表单里只有两个东西:输入框和按钮。转写里的要求是:input 宽高 490×32,内边距上下 0、左右 4,边框 2px;button 宽高 68×32,背景用前面定义好的红色变量,文字白色,去掉自带边框。关键点是 input 要设 box-sizing: border-box,否则加了边框后总宽会超过 490。按钮转写里特别提醒:button 默认就是 border-box,不用再单独设。示例:
less
.search-form {
display: flex;
margin-top: 35px;
.search-input {
width: 490px;
height: 32px;
padding: 0 4px;
border: 2px solid @form-red;
box-sizing: border-box;
outline: none;
}
.search-btn {
width: 68px;
height: 32px;
color: #fff;
background: @form-red;
border: none;
}
}为什么 form 也要 Flex?转写调试时发现 input 和 button “对不齐”:它们中间有换行产生的空白,靠传统方式还要处理 white-space。直接把 form 变成伸缩容器,两个子项并排,问题就没了。易错点:input 不设 box-sizing 会变宽;不去 outline,聚焦时会有黑框;按钮不去边框会多出一圈;form 的 margin-top: 35px 是给整体加的,不要错加到 input 上。
调试与易错点
刷新后如果“啥也没有”,先查 HTML 是否引入 index.css,再查 index.less 是否编译。转写里还出现“写到这儿可不行”的情况:样式嵌套位置写错,导致选择器没命中。Less 嵌套要照着 HTML 结构写,但不要无脑套太多层;变量和混合文件不单独编译,只编译入口 index.less。最后记住:编译后的 CSS 可读性低、注释也可能没有,调试时看 Less 源文件,不要盯着编译结果猜。
课堂画面






代码示例
示例 1:整体准备——Less 多文件拆分 + 入口导入
html
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>PC项目-商品详情</title>
<!-- 只引入编译后的结果文件 -->
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!-- 页面结构:topbar / header / nav / 主内容区,一部分一部分往下加 -->
</body>
</html>less
/* ========== css/variables.less 只放变量 ========== */
// 表单红:搜索按钮背景、input 边框
@form-red: #ea4a36;
// 按钮红:全部分类等按钮背景
@btn-red: #e1251b;
// 分割线颜色
@sep-color: #eaeaea;less
/* ========== css/mixins.less 只放混合 ========== */
// 带括号,不调用就不编译输出
.clearfix() {
&::after {
content: "";
display: block;
clear: both;
}
}less
/* ========== css/index.less 总入口 ========== */
@import "variables.less"; // 导入变量
@import "mixins.less"; // 导入混合
@import "reset.less"; // 导入重置样式(原 reset.css 改名为 .less)
// 版心:各部分通用
.container {
width: 1200px;
margin: 0 auto;
}要点:
reset.css的扩展名要改成.less。用@import "xxx.css"时,编译结果里会原样保留这条@import语句;改成.less才会把内容真正“拿进来”合并进index.css。- 混合(mixin)定义时一定要带括号
.clearfix(),否则它会被当作普通类名编译输出;调用时写成.clearfix();,用在哪个元素里,就在哪个元素里生成::after,不是靠叠加类名。 - 变量名用
@开头(@form-red),两个红要分清:@form-red只给表单用(按钮背景 + input 边框),@btn-red给全部分类等按钮用,混用会导致色差。 - 只需编译
index.less,生成index.css后 HTML 只引这一个文件;HTML 里不要再单独引入reset.css。 - 变量、混合这两个文件本身不需要编译,它们的内容只会通过入口文件的导入语句进入
index.css。
示例 2:topbar——伸缩盒两端对齐 + 用边框做竖线
html
<!-- index.html 中 body 内 -->
<div class="topbar">
<div class="container">
<div class="topbar-left">
<span>欢迎来到商品会</span>
<a href="#">登录</a>
<a href="#">免费注册</a>
</div>
<div class="topbar-nav">
<a href="#">我的订单</a>
<a href="#">我的购物车</a>
<!-- 其余导航链接按资料依次补全 -->
</div>
</div>
</div>less
/* 追加到 index.less */
.topbar {
min-width: 1200px; // 视口比版心窄时,背景色不断裂
height: 30px;
line-height: 30px;
background-color: #eaeaea;
.container {
display: flex;
justify-content: space-between; // 左欢迎语、右导航一左一右
}
// 左右两边的 A 统一内边距
a {
padding: 0 10px;
// 排除各自父元素里的最后一个
&:not(:last-child) {
border-right: 1px solid @sep-color;
}
}
}要点:
- 背景色和下边框是通栏的,所以
min-width加在最外层.topbar上,不加在.container上;.container本身固定 1200,不要再设min-width。 - 竖线不用额外元素、也不用列表,直接给
a加border-right;&:not(:last-child)能同时排掉左右两组里各自的最后一个a。 &表示“当前选择器”,&:not(:last-child)编译成a:not(:last-child);不写&就会变成后代选择器,选择器会失效。- 竖线颜色用变量
@sep-color,不要手写十六进制,否则以后统一换色会漏改。 - 忘记
display: flex时,.topbar-left和.topbar-nav是块元素会上下叠,看不到一左一右的效果。
示例 3:logo 和搜索框——宽高按“总宽高”算
html
<!-- index.html 中 topbar 之后 -->
<div class="header">
<div class="container">
<a href="#" class="logo">
<img src="./images/logo.png" alt="logo">
</a>
<form class="search">
<input type="text" class="search-input">
<button type="button" class="search-btn">搜索</button>
</form>
</div>
</div>less
/* 追加到 index.less */
.header {
min-width: 1200px;
.container {
display: flex;
justify-content: space-between; // logo 靠左,表单靠右
}
.logo {
width: 175px;
height: 56px;
margin: 25px 45px;
img {
width: 100%;
height: 100%;
}
}
.search {
display: flex; // 消掉 input 与 button 之间换行产生的空白
margin-top: 35px;
input {
box-sizing: border-box; // 宽高按“总宽高”算
width: 490px;
height: 32px;
padding: 0 4px;
border: 2px solid @form-red;
outline: none; // 去掉聚焦时的黑框
}
button {
width: 68px;
height: 32px;
background-color: @form-red;
color: #fff;
border: none;
}
}
}要点:
input必须写box-sizing: border-box,否则 490 是内容宽度,再加上左右 2px 边框,总宽会变成 494,和按钮对不齐;button默认就是border-box,不用再设。input和button是行内块元素,HTML 里换行会留一个空格;最省事的办法是把.search变成display: flex,不要再用font-size: 0或float。a是行内元素,但作为 flex 项目(.logo)可以正常设置宽高,175×56 是图片尺寸,margin: 25px 45px是上下 25、左右 45。- 边框、按钮背景都用变量
@form-red;input要写outline: none,否则聚焦时外面会多出一圈黑框,很丑。 - 表单用
margin-top: 35px整体下移,不要给input单独加margin-top,否则按钮会错位。
常见错误
- 现象:页面完全无样式,控制台也没有明显报错。原因:HTML 直接引入
index.less,或把reset.css改成.less后仍按 CSS 路径引入。修法:HTML 只引入编译后的index.css;reset.less、变量、混合统一在入口index.less中导入,只编译入口文件。 - 现象:变量文件或混合文件单独编译报错/生成空文件。原因:把变量、混合文件当成独立入口编译;混合定义未加括号。修法:只编译
index.less;混合定义写成.clearfix() {}形式,变量/混合文件仅通过入口导入。 - 现象:窗口缩到 1200px 以下时,topbar 背景或下边框突然断掉。原因:背景加在外层容器上,外层宽度跟随视口,而版心固定 1200px 溢出。修法:给对应外层容器加
min-width: 1200px,不要给版心自身加固定宽度。 - 现象:topbar 左右区域没有一左一右,或右侧 A 的竖线多一条/少一条。原因:父级未设
display:flex + justify-content:space-between;A 的边框未排除最后一项。修法:父级设 flex 两端对齐;A 统一内边距和右边框,用&:last-child去掉最后一项边框。 - 现象:搜索框 input 和 button 对不齐,中间有空白,按钮自带边框很丑。原因:行内元素换行产生空白;input 未设
box-sizing;button 未清默认边框。修法:form 设display:flex;input 设box-sizing:border-box;button 设border:none,input 设outline:none。 - 现象:路径导航最后一个分类后面仍有斜杠,或空白区域也能点。原因:伪元素加在 A 上而不是文字 span 上;未排除最后一个 span。修法:用 span 包文字,斜杠伪元素加到 span 上,排除最后一项;span 设 flex 去掉换行空白。
- 现象:放大镜大图溢出、位置不对,蒙层定位到页面其他位置。原因:大图未设
overflow:hidden,大图/蒙层未绝对定位,父级未设相对定位。修法:大图容器设overflow:hidden;zoom-box和小图容器设position:relative;大图和蒙层设position:absolute,蒙层先display:none。
练习
- 搭建 LESS 入口:新建
variables.less、mixins.less、reset.less、index.less,在入口中按顺序导入,HTML 只引index.css;定义表单红、按钮红、分割线颜色三个变量,并写一个清除浮动混合。 - 完成 topbar:外层加版心,左右两块用 flex 两端对齐;所有 A 加左右 10px 内边距,右边框使用分割线变量,最后一项去掉边框;把视口缩小到 1200px 以下检查背景是否断裂。
- 完成 logo 与搜索框:logo 设 175×56、外边距 25px 45px;form 设 flex 和上外边距 35px;input 设 490×32、
box-sizing:border-box、左右内边距 4px、2px 边框;button 设 68×32、无边框、背景和文字颜色正确。 - 完成页面导航:container 设 flex 和高度 50px;分类区宽 210px,H2 行高 50px、文字居中;右侧导航 A 设左右 22px、字号 16px、颜色 333;检查 LESS 嵌套层级是否写到正确父级下。
- 完成路径导航与放大镜布局:路径导航用 span 包文字,非最后一项加斜杠伪元素,最后一项无斜杠;放大镜区域 400×400,大图绝对定位到右侧并
overflow:hidden,蒙层 200×200 绝对定位且默认隐藏。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 整体准备 | 09:02 |
| 2 | topbar | 13:03 |
| 3 | logo和搜索框 | 06:32 |
| 4 | 页面导航 | 07:17 |
| 5 | 路径导航 | 17:36 |
| 6 | 放大镜区域-布局 | 17:39 |
小结
- 分文件要克制:变量、混合、重置、入口四类足够,入口文件是唯一编译对象。
- 颜色变量优先抽取,尤其是反复出现的红色和分割线色;尺寸变量按需再加。
- 每完成一个模块就单独保存文件,出问题能快速定位和回退。
- 布局优先用 flex,能同时解决一左一右、行内空白和对齐问题,减少浮动与清除浮动。
- LESS 嵌套必须跟着 DOM 层级写,选择器写复杂后先看编译结果再判断对错。
- 版心外层要设最小宽度,否则窄视口下背景和下边框会断裂。
- 定位区域先确定包含块,再写绝对定位子元素;图片溢出先查
overflow:hidden。 - 伪元素做分隔符时,作用域尽量放在文字 span 上,并排除最后一项,交互区域才准确。