☰
仿小米商城静态页面实战:HTML+CSS布局、Flex栅格与响应式适配全解析
2026/10/13 4:58:55 网站建设 项目流程

简介:这是一份仿小米商城Web前端实战练习包,适合刚接触HTML和CSS的初学者,也适合作课程结业练习。资源围绕首页结构设计展开,包含header导航、轮播图、商品分类卡片、页脚等典型电商模块,重点训练盒模型、浮动、Flexbox/Grid布局、响应式查询,并用语义化标签组织页面。压缩包共410个文件、体积3.28MB,图片素材占比最高:248张jpg用于商品与轮播图,98张webp和56张png用作图标和背景;另有4个CSS样式表、2个JS脚本和1个HTML入口页面,附带reset.css与iconfont字体图标,文件分类清晰。目前已有906人学习下载,适合对照源码逐步拆解页面实现。参考这套代码,可快速了解电商首页从布局、样式到简单交互的搭建流程,也能体会CSS模块组织、命名规范以及图片资源按用途拆分的常用思路,为后续Web开发练习提供一个可借鉴的起点。

1. 仿小米商城 wed 练习:一个 HTML+CSS 页面能解决的问题

标题里的 wed 一眼就能看出是 web 的手滑,但练的事没跑偏:用纯 HTML 和 CSS 把小米商城首页的静态结构复刻出来。这个练习真正解决的不是“我会写标签和样式”,而是让你在只有一个浏览器和编辑器的条件下,把页面分区、公共样式抽取、间距统一、响应式适配这一整条前端基本功走一遍。适合刚学完 HTML 标签、CSS 选择器和盒模型,却还没独立写完一个完整页面的人;也适合面试前想快速攒一个拿得出手的静态项目的求职者。纯静态页面没有接口、没有登录态,所有数据都是写死的,所以你能把注意力全放在布局和样式上,而这正是很多实战项目里最容易稀里糊涂混过去的环节。

2. 先用 HTML 把三层骨架搭出来:语义化标签与区块规划

2.1 页面总结构:header、main、footer 每层装什么

写页面和盖房子一样,先框架后装修。我一般会先建一个空 HTML 文件,把 body 里三层大结构写出来,再逐层填内容。header 装顶部导航、logo、搜索框;main 装轮播图、商品分类入口、商品列表;footer 装底部链接和版权信息。这样后头写 CSS 时,每一块的作用域都非常清楚,样式不会互相串。

<!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="css/style.css"> </head> <body> <header class="site-header"> <!-- 顶部导航 + 搜索区 --> </header> <main class="site-main"> <!-- 轮播图、商品分类、商品列表 --> </main> <footer class="site-footer"> <!-- 多列链接 + 版权信息 --> </footer> </body> </html>

三个语义化标签是 HTML5 里最基础也最容易被忽略的部分。header、main、footer 不只是“看起来像那么回事”,它们对屏幕阅读器和搜索引擎抓取都有实际意义。main 一个页面原则上只出现一次,表示主体内容;header 和 footer 可以出现在页面级,也可以出现在某个区块级。这里 class 命名用 site- 前缀,是为了和区块内部的 header、footer 区分开,避免后头样式打架。

2.2 顶部导航与搜索区的 HTML:为什么用 list 而不是 div

顶部导航看似简单,却是新手第一个容易翻车的地方。很多人习惯每个链接放一个 div,或者干脆用一堆 a 标签平铺。常见做法应该是:导航项是一组同类条目,用 ul 列表承载,每个 li 里放一个 a。这样屏幕阅读器会把它读成一个列表,CSS 里用 flex 一行排开也顺手,后头想加分隔线、hover 背景都只需要操作一个选择器。

<div class="header-topbar"> <ul class="topbar-links"> <li><a href="#">小米商城</a></li> <li><a href="#">MIUI</a></li> <li><a href="#">云服务</a></li> <li class="topbar-right"><a href="#">登录</a></li> <li class="topbar-right"><a href="#">注册</a></li> </ul> </div> <div class="header-main"> <div class="logo">Mi</div> <nav class="category-nav"> <ul> <li><a href="#">手机</a></li> <li><a href="#">电视</a></li> <li><a href="#">家电</a></li> <li><a href="#">智能家居</a></li> </ul> </nav> <div class="search-box"> <input type="text" placeholder="笔记本"> <button type="button">搜索</button> </div> </div>

搜索框这里我用了 div 包 input 和 button,而不是 form。纯静态练习场景下,没有提交行为,用 div 能少一层默认表单样式干扰;但如果你想更规范,应该改用 form 并给 button 加 type="submit"。topbar-right 这类修饰类名,作用是让登录注册靠右,而不用给每个 li 单独写一长串 style。占位符 placeholder 写“笔记本”这种真实搜索词,比“请输入关键词”更能还原页面观感。

2.3 轮播与商品区的容器设计:先定尺寸再填内容

轮播区和商品区是首页最占面积的模块,也是布局最容易出问题的两块。常见做法是给轮播区域一个确定高度的容器,商品区按一行四列设计。写 HTML 时先把容器和列表结构立起来,图片先用占位路径,后头再用真实图片替换。这一步的坑是很多人一上来就填一堆内容,结果结构乱了还以为是 CSS 的问题。

<section class="banner-section"> <div class="banner-track"> <div class="banner-slide" style="background-image: url('img/banner-1.png');"></div> <div class="banner-slide" style="background-image: url('img/banner-2.png');"></div> <div class="banner-slide" style="background-image: url('img/banner-3.png');"></div> </div> </section> <section class="product-section"> <h2 class="section-title">热门手机</h2> <ul class="product-grid"> <li class="product-card"> <img src="img/phone-1.png" alt="Redmi Note 12"> <h3>Redmi Note 12</h3> <p class="price">¥1299</p> </li> <li class="product-card"> <img src="img/phone-2.png" alt="Xiaomi 15"> <h3>Xiaomi 15</h3> <p class="price">¥3999</p> </li> </ul> </section>

banner-track 是轮播的“轨道”,banner-slide 是每一张图,这种命名对应后续 CSS 动画的位移逻辑。商品列表用 ul 包 li,是因为每个卡片都是同级条目,语义上和导航同理。h2 的 section-title 不是摆设,它明确告诉浏览器这一块的主题是什么。img 的 alt 属性不能省略,图片加载失败时用户能看到文字说明,屏幕阅读器依赖它描述图片。我见过太多练习代码里的 alt 是空的,这习惯不好,现在就改掉。

3. CSS 三条主线:变量、Flex 与栅格怎么配合

3.1 先定 CSS 变量:主题色、间距、圆角一次到位

HTML 骨架搭完后,别急着去调每个模块的样式,先把一套全局变量定下来。小米商城标志性的橙色、正文灰色、背景浅灰、边框色、导航栏高度、内容区宽度、卡片圆角,这些值会在几十个选择器里反复出现。写死在每个规则里,改一次主题色等于全表查找替换,用 CSS 自定义属性就没这个烦恼。

:root { --primary-color: #ff6700; --text-main: #333; --text-secondary: #888; --bg-light: #f5f5f5; --border-color: #e5e5e5; --header-height: 40px; --container-width: 1240px; --radius: 8px; --gap-base: 16px; } body { margin: 0; font-family: "Helvetica Neue", Arial, sans-serif; color: var(--text-main); background: var(--bg-light); }

变量名不要写 color-1、color-2 这种没语义的编号,要用用途命名。--primary-color 一看就是主题主色,--text-secondary 是次级文字颜色。body 里的 margin: 0 是基础 reset 的一部分,不然浏览器会给 body 默认 8px 外边距,整个页面四周都会有一条白边。font-family 写了无衬线字体栈,页面气质贴近电商风格。这里顺手把后续要用到的间距和圆角都定义好,写模块时直接用 var() 取,数值统一,页面看起来才会“整”。

3.2 Flex 管一行对齐:导航、搜索框、卡片头部

页面里大量场景是“一行内左右分布”或“一行内水平居中”,Flex 是解决这类问题最顺手的工具。header-main 里 logo 在左、导航在中间、搜索框在右,一个 display: flex 加 justify-content: space-between 就搞定。align-items: center 保证三者在不同高度时垂直居中,后头想调高度只需要改一个值。

.header-main { display: flex; align-items: center; justify-content: space-between; width: var(--container-width); margin: 0 auto; height: 80px; } .category-nav ul { display: flex; gap: 24px; list-style: none; padding: 0; margin: 0; } .search-box { display: flex; align-items: center; } .search-box input { width: 220px; height: 36px; border: 1px solid var(--border-color); border-radius: var(--radius); padding: 0 12px; font-size: 14px; } .search-box button { height: 36px; background: var(--primary-color); color: #fff; border: none; border-radius: var(--radius); padding: 0 16px; margin-left: 8px; cursor: pointer; }

justify-content: space-between 让 logo、导航、搜索框三者自动贴向两端和中间,不用手动算 margin。gap 是 Flex 布局里控制子元素间距的属性,比在每个 li 上加 margin-right 干净得多,而且不会多出最后一个元素的尾间距。search-box 内部又套了一层 flex,这是因为 input 和 button 需要垂直方向对齐。注意 list-style: none 不能忘,ul 默认带圆点,不干掉的话导航里会出现一排黑点。

3.3 栅格管多列:商品列表为什么优先选 grid 而不是 float

商品卡片是典型的多列网格,最常见做法是 display: grid,而不是那些老项目里的 float。grid 的优势在于:列数和间距一行声明,子元素自动填充到每一列,响应式修改时只改列数,不需要去子元素上挨个调宽度。float 布局要做到同样的效果,必须手动算百分比宽度,还要处理父容器高度塌陷的清除浮动问题,新手用 float 做卡片列表十有八九会翻车。

.product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-base); list-style: none; padding: 0; margin: 0; } .product-card { background: #fff; border-radius: var(--radius); padding: 16px; transition: box-shadow 0.3s ease; } .product-card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }

repeat(4, 1fr) 的意思是生成 4 列,每列宽度平分剩余空间,1fr 是 fr 单位,表示一份可用空间。gap 同时控制行和列间距,想区分可以用 row-gap 和 column-gap。product-card 的 hover 阴影过渡是电商卡片常见的微交互,transition 写在默认状态里,hover 只改属性值,这样才能有动画效果。把这段跑起来,卡片会自动排成 4 列,窗口变窄时溢出而不是换行,这留给响应式章节处理。整套栅格结构里不需要给单个卡片算宽度,这是 grid 比 float 省心最直接的地方。

4. 模块逐个落地:轮播、商品卡片与页脚的细节处理

4.1 纯 CSS 轮播:用 animation 做定时位移,注意回跳现象

轮播是这个项目里最亮眼的部分。常见做法是纯 CSS animation 实现自动切换,不写一行 JavaScript。原理是把所有幻灯片水平排列在一条轨道里,轨道宽度是幻灯片数量的倍率,然后通过动画改变轨道在容器内的位移百分比,让不同位置的图依次进入可视区域。第一次写的时候很多人会把 translateX 的百分比算错,记住它移动的是轨道自身宽度。

.banner-section { overflow: hidden; height: 340px; } .banner-track { display: flex; width: 300%; height: 100%; animation: banner-slide 12s infinite; } .banner-slide { width: calc(100% / 3); height: 100%; background-size: cover; background-position: center; } @keyframes banner-slide { 0%, 30% { transform: translateX(0); } 33%, 63% { transform: translateX(-33.33%); } 66%, 96% { transform: translateX(-66.66%); } 100% { transform: translateX(0); } }

banner-track 宽度 300%,因为里面放了 3 张幻灯片,每张占轨道宽度的三分之一,也就是容器宽度的 100%。轨道位移 -33.33% 等于向右移动一张图的宽度。动画时间线里 0%-30% 停留第一张,33%-63% 停在第二张,66%-96% 停在第三张,最后 100% 瞬间回到起点,这个设计目的是让每张图都有充足停留时间而不是均匀滚动。坑在于最后回到 0 是跳变,不无缝,这个在后面避坑章节细说。想加轮播指示器小圆点,再放一层绝对定位的按钮就行,这里不展开。

4.2 商品卡片三件套:图片比例、标题截断、价格对齐

商品卡片是首页密度最高的模块,也是最容易因细节粗糙而显得业余的地方。三个核心细节必须处理到位:图片统一比例、标题过长时截断成省略号、价格颜色和字号突出。图片比例不固定会导致一排卡片高低不齐;标题长短不一则会让卡片底部参差;价格不用主题色就抓不住眼睛。这三件事用最短的代码做完,整张卡片就立住了。

.product-card img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: calc(var(--radius) / 2); } .product-card h3 { font-size: 14px; color: var(--text-main); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 12px 0 8px; } .product-card .price { color: var(--primary-color); font-size: 18px; font-weight: 600; margin: 0; }

aspect-ratio: 1 / 1 是让图片容器强制保持正方形,object-fit: cover 让图片在变形和裁剪之间选择裁剪,既保证比例又铺满空间。标题三件套是 white-space: nowrap、overflow: hidden、text-overflow: ellipsis,缺一不可,第一行禁止换行,超出部分隐藏,隐藏部分显示为三个点。这里有个新手常犯的失误:text-overflow 只在块级元素且 overflow 不为 visible 时生效,所以 display: inline 的元素光写这三句是没用的。价格单独用主题色加粗,视觉上自然形成层级,不用额外加边框或背景去强调。

4.3 页脚多列:dl 列表与 grid 分栏的搭配

页脚看似不起眼,却是新手练习语义化标签的好机会。常见的链接列表布局是四列,每列有个小标题加若干链接。小标题和链接在 HTML 语义上适合用 dl(描述列表)承载,dt 是标题,dd 是内容项。用 dl 而不是 ul 的原因很直接:这里的结构是“一个标题对应多个链接”,有成组关系,ul 表达不了这层意思。

<footer class="site-footer"> <div class="footer-links"> <dl> <dt>帮助中心</dt> <dd><a href="#">购物指南</a></dd> <dd><a href="#">支付方式</a></dd> <dd><a href="#">运费说明</a></dd> </dl> <dl> <dt>服务支持</dt> <dd><a href="#">售后政策</a></dd> <dd><a href="#">自助服务</a></dd> <dd><a href="#">相关下载</a></dd> </dl> </div> <div class="footer-copyright"> <p>© 2025 仿小米商城练习项目</p> </div> </footer>
.site-footer { background: #fff; border-top: 1px solid var(--border-color); padding: 40px 0 24px; margin-top: 40px; } .footer-links { display: grid; grid-template-columns: repeat(4, 1fr); width: var(--container-width); margin: 0 auto; } .footer-links dt { font-weight: 600; font-size: 14px; margin-bottom: 12px; } .footer-links dd { margin: 0 0 8px; } .footer-links a { color: var(--text-secondary); font-size: 13px; text-decoration: none; } .footer-links a:hover { color: var(--primary-color); } .footer-copyright { text-align: center; color: var(--text-secondary); font-size: 12px; margin-top: 24px; }

footer 里的 grid 和商品区不同,这里设置宽度再用 margin: 0 auto 水平居中,而不是铺满全屏,这样与上面内容区对齐。dt 加粗拉开层级,dd 默认有缩进,需要手动把 margin 清零,不然左边会多出一截,这是新手很容易忽略的细节。页脚链接 hover 时变主题色,和全局交互语言保持一致。最后版权区单独一行居中,颜色弱化,按视觉层级排在最后,整个页脚就是这个项目整体结构的收尾。

5. 样式重置与响应式:移动端适配的避坑清单

5.1 先做 reset:浏览器默认样式差异比你想的大

不同浏览器的默认样式表并不一致,最典型的就是 body 的 margin、ul 的 padding 和 list-style、h1 到 h6 的字号差异。如果不清掉这些默认值,页面在 Chrome 里正常,换到 Safari 或 Firefox 就可能出现几像素偏移,或者列表圆点冒出来。常见做法是在写业务样式之前放一份 reset,最精简的版本是全局清零关键属性。

* { margin: 0; padding: 0; box-sizing: border-box; } ul, ol { list-style: none; } a { text-decoration: none; color: inherit; } img { max-width: 100%; display: block; }
  • 选择器把所有元素的内外边距清零,box-sizing: border-box 是更关键的一行。它让元素的 width 属性包含 padding 和 border,而不是额外加宽,否则 100% 宽度加上 16px padding 会超出容器,出现横向滚动条。img 的 display: block 是去掉图片底部的几像素空白间隙,这个间隙来自图片作为行内元素和文字基线对齐产生的空间。我把 reset 放在 css 文件最前面,再用自己的类名覆盖,这样不会影响后面任何模块的样式设计。

5.2 断点别拍脑袋:内容什么时候换行就定哪个断点

响应式设计的核心是媒体查询,但断点怎么选,很多人的做法是随便挑 768px、1024px 各写一套,结果平板横屏时页面又空又不整齐。我一般不建议按设备型号定断点,而是拉窄浏览器窗口,观察内容在哪个宽度开始挤、卡片哪个位置开始错位,在它出问题的前一个宽度落断点。这样写出来的媒体查询是跟着内容走的,而不是被某个设备绑架。

@media (max-width: 1024px) { .product-grid { grid-template-columns: repeat(3, 1fr); } .header-main { width: 100%; padding: 0 16px; box-sizing: border-box; } } @media (max-width: 768px) { .product-grid { grid-template-columns: repeat(2, 1fr); } .banner-section { height: 240px; } .category-nav { display: none; } }

1024px 时 4 列卡片还能勉强放下,但间距已经变得很窄,改成 3 列更从容。768px 是手机与平板的分界线附近,这里 2 列排布最舒适,轮播高度从 340px 降到 240px,减少小屏上的空间浪费。category-nav 在手机端直接隐藏,是因为容纳不了 5 个导航项,与其换行破坏高度,不如藏起来,真实商城一般会换成汉堡菜单,但那需要 JavaScript,纯 CSS 练习阶段不做也行。媒体查询不是越多越好,能用 3 个断点解决的问题不要写 5 个。

5.3 五个常见翻车现场:现象、原因、解决办法

现象一:轮播区高度塌成一条线,背景图怎么都显示不出来。原因是 banner-slide 用 background-image 时,自身没有高度,父级 .banner-section 的高度没被子元素撑起来,或者子元素写了 float 导致父容器高度归零。解决方法是给 .banner-section 写固定 height 或 min-height,轨道和幻灯片都设为 height: 100%,而不是依赖内容撑开。

现象二:搜索按钮和输入框对不齐,上下差两三个像素。原因往往是 input 和 button 的默认字体大小、padding、border 不一致,浏览器对表单控件有额外样式。解决方法是给两者设置相同的 height、font-size、padding,并把 input 的 border-right 去掉或统一 border,再用 flex align-items: center 包一层。

现象三:商品卡片最后一行只有两个,整个底部对不齐。原因是卡片文字行数不同,或者图片比例不一致,导致卡片高度参差。grid 默认每个格子等高、内容独立,不会补齐矮卡片。解决方法是给图片统一 aspect-ratio、标题固定单行截断,卡片内部 padding 一致,让所有卡片高度由内容撑到同一条水平线。

现象四:下拉菜单或悬浮框被后面的轮播图盖住。原因是两个元素都设置了 position 定位,但只有轮播区的 z-index 生效。解决办法很简单,给顶部下拉容器加 z-index: 100,并保证它和轮播区不是同一个定位上下文。别乱写大数值 z-index,100 已经够用,写 9999 只会让自己后头分不清层级关系。

现象五:响应式后卡片宽度不对称,出现一条缝。原因是子元素里有固定 min-width,或者给卡片设了 width: 25% 又被 padding 撑破。grid 布局下不要再同时给子项设宽度或 flex-basis,让它完全交给列轨道控制。检查容器是否 box-sizing: border-box 也是必要一步,很多人就是漏了这一行,然后所有百分比宽度全部穿帮。

提示:翻车排查有个顺序,先看 HTML 结构有没有闭合,再看 reset 有没有生效,最后才动业务样式。直接改 CSS 经常是治标不治本,过两天换个浏览器又复现。

6. 进阶:把页面拆成组件化结构,再按自检清单过一遍

做到这一步,页面已经能完整展示,但它还只是一堆写在同一个 HTML 里的区块。想让这些代码具备复用价值,可以按组件的思路重构一遍:把商品卡片、导航栏、页脚链接列各抽成独立的 CSS 类,用类似组件命名的方式组织。比如商品卡片使用 product-card 作为基础类,hover 后的阴影状态用 product-card--highlight 修饰,这样在 HTML 里复制十个卡片,只需要保证类名不变,样式就全部绑定。

这种写法能直接带来一个好处:你改一处卡片样式,所有卡片同步更新,不用在一个元素一个元素地找 class。重构时可以顺便给 CSS 文件分段整理,用注释标出变量区、reset 区、布局区、组件区,后头想换主题色,只需要改 :root 里的 --primary-color 一个值。页面里所有引用 var(--primary-color) 的地方都会跟着变,这就是变量体系的意义。

完成重构后,我会过一遍自检清单:第一,用浏览器开发者工具的手机模拟模式,分别看 375px、768px、1024px 三档宽度有没有横向滚动条;第二,检查所有图片是否有 alt 文案,这是可访问性最基本的要求;第三,把图片路径临时换成不存在的路径,看布局会不会因为图片加载失败而塌掉,如果塌了就说明高度写死还不够彻底,应该用 aspect-ratio 或固定容器兜底;第四,确认所有超链接 href 虽然都是 #,但至少能看出来指向关系,不至于让审阅者觉得是五个死链摆在那。

最后把 HTML 文件直接拖进浏览器,按 Ctrl+Shift+I 打开控制台,确认没有任何报错。我当初第一次练这个项目时,就是栽在最后一步:轮播正常、卡片正常,但控制台一堆 404,因为图片路径写错,纯粹是偷懒没验。从那以后我就养成一个习惯,每写完一个模块先刷新再看控制台,而不是全部写完才回头查。这行习惯帮我省下的排查时间,比任何一个 CSS 技巧都值钱,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询