简介小米官网首页静态页面是一份适合前端初学者实战练习的网页源码包覆盖HTML结构搭建与CSS样式设计两大基础模块。整个项目模拟小米官网首页的视觉风格与布局框架能帮助学习者理解常见标签、类与ID选择器、盒模型、Flexbox/Grid布局、媒体查询以及:hover等伪类的实际用法并对页面间距、字体、背景图片等细节有直观体验。压缩包共38个文件约2.64MB其中2个html文件为页面入口8个css文件包含样式表及压缩版本jpg/png/svg图片素材用于产品展示与图标ttf/woff/woff2等字体文件保证页面字体效果完整目录结构清晰便于直接运行、改版和对照学习。已有667人学习下载。借助这份静态页面读者能够了解电商首页的代码组织方式学会合理拆分CSS、优化资源引用为后续学习响应式布局与前端交互开发积累扎实基础。1. 为什么练 HTML/CSS 首选小米官网首页像素级还原的性价比之选前端入门有个绕不开的坎——练手项目选什么。很多人一上来就写个人博客结果自由度太高配色、布局、内容全凭感觉最后练出来的是「会搭积木」而不是「会照着图纸施工」。而小米官网首页这个静态页面项目是 HTML CSS 综合练习里性价比极高的一个布局密度大、组件类型多、黑灰白配色下藏着大量 hover 状态和层级细节能同时练到栅格、定位、导航、轮播、卡片化信息架构这几块硬功夫。这份资源重建了官网首页的主视觉与核心信息结构用纯 HTML CSS 搭出顶部导航、轮播图、商品卡片区、限时活动入口和页脚等完整模块。它解决的正是新手最缺的两件事一是像素级还原能力——对着设计稿把间距、字号、圆角、 hover 效果逐个抠出来二是结构组织能力——一个页面几十个模块怎么划分区块、怎么命名 class 才能不混乱。适合学过 HTML 和 CSS 基础标签、想找一个完整项目来检验自己水平的人也适合前端讲师拿来做教学案例。别小看这个页面能把首页还原到 90% 以上你对盒模型和 Flex 布局的理解会远超刷一百道题。2. 拆开看这份资源目录结构、文件职责与还原度边界2.1 文件组织静态页面的骨架与前两次打开的差异拿到压缩包先别急着双击index.html先花两分钟把目录结构过一遍。这个项目的文件划分是典型的前端小项目布局——HTML 管内容、CSS 管表现、JS 管交互三者各司其职。我第一次拆的时候没看结构直接打开页面F12 一查发现样式全堆在style里后来才发现那是旧版本这份资源的结构要干净得多适合直接上手学。路径职责关键内容index.html页面主体结构导航、轮播、商品卡片、页脚的所有 DOM 节点css/reset.css样式重置清除浏览器默认 margin/padding、统一字体基线css/style.css核心样式布局栅格、组件样式、hover 状态、媒体查询js/banner.js轮播逻辑自动播放、指示器切换、图片渐隐渐现img/静态资源商品图、banner 图、图标等本地图片reset.css和style.css分开这个设计很关键。reset负责把不同浏览器默认样式拉齐比如 Chrome 和 Safari 对body的 margin 理解不一样不重置的话页面会在浏览器之间产生几像素偏移style.css才放业务样式。如果你看到有的教学项目把 reset 和业务样式混在一个文件里那后期维护时会很痛苦——你根本分不清哪个样式是清默认值的哪个是设计稿要求的。JS 文件只有banner.js一个约 40 行这也是静态页面的常态交互越少越能把精力放在 CSS 上。另外注意img/目录里的图片都是本地文件不是外链。这一点很实在意味着你断网也能打开页面也意味着你可以直接替换图片来测试布局不用处理防盗链和跨域问题。2.2 先看 HTML 主体骨架三大区块的划分逻辑打开index.html你会看到结构整体分为三个大块顶部导航区、中间内容区、底部页脚区。中间内容区又可以细分为 banner 轮播、商品楼层、活动专区三小块。这种分区方式不是随便切的它和页面视觉层次完全对应——用户从上往下扫视眼睛依次经过品牌区、焦点图、商品陈列、服务保障HTML 的书写顺序也应该遵循这个阅读顺序。body !-- 顶部导航包含 logo、菜单、搜索、登录入口 -- header classsite-header div classcontainer nav-wrapper div classlogo a href#img srcimg/logo.png alt小米/a /div ul classnav-list lia href#手机/a/li lia href#电视/a/li lia href#笔记本/a/li lia href#家电/a/li /ul div classsearch-box input typetext placeholder搜索 button classsearch-btn/button /div /div /header !-- 主内容区banner 轮播 商品卡片楼层 -- main classsite-main section classbanner-section div classbanner-carousel idbannerCarousel div classbanner-slide active a href#img srcimg/banner-1.jpg alt/a /div div classbanner-slide a href#img srcimg/banner-2.jpg alt/a /div /div !-- 轮播指示器 -- div classbanner-dots span classdot active/span span classdot/span /div /section section classproduct-floor div classcontainer h2 classfloor-title热门手机/h2 div classproduct-grid !-- 商品卡片由 JS 或模板渲染也可直接书写静态结构 -- div classcard img srcimg/phone-1.png alt p classcard-name小米 14/p p classcard-price3999元/p /div div classcard img srcimg/phone-2.png alt p classcard-nameRedmi K70/p p classcard-price2499元/p /div /div /div /section /main !-- 页脚服务保障、合作链接、备案信息 -- footer classsite-footer div classcontainer footer-links a href#新手帮助/a a href#服务网点/a a href#售后政策/a /div div classcontainer copyright p© 2024 小米公司版权所有/p /div /footer /body上面这段是简化后的骨架但关键节点都在。注意header .nav-wrapper这一层嵌套.container是固定宽度的居中容器通常是 1226px和官网一致它把内部内容约束在一个安全可视区内防止屏幕太宽时元素被拉伸到离谱的位置。.nav-list用无序列表承载菜单每个菜单项是lia结构——这是语义化菜单的标准写法方便屏幕阅读器识别。banner-slide用active类来控制当前显示哪一帧这个类名是 JS 切换的抓手。商品楼层.product-grid里每一张卡片由图片、名称、价格三行构成注意卡片内容没有多余装饰良好的结构能让你后续写 CSS 时少折腾很多。2.3 还原度边界静态页面不是实时官网拿到资源后要有个预期管理官网是持续迭代的今天打开小米官网看到的视觉和你下载的这份静态页面大概率有差异。这份资源是对某一个时间节点官网首页的重建它的价值在于让你练习那个版本的还原手法而不是和现在的官网逐像素对比。我见过有初学者对着最新的官网截图去改这份代码结果越改越乱——因为新版官网的布局、配色、甚至 logo 字体都变了。正确的用法是把这份资源当作设计稿的替代品先本地跑起来再按自己的理解去调整间距和配色。你要学的不是复制某个版本的官网而是掌握「看一个页面能拆出结构、能用 CSS 还原出来」这个能力。这也是为什么我一再强调先读懂文件结构再动手——你后续所有改动都建立在这个结构之上结构不熟改哪都是盲改。3. 像素级还原的三个核心模块栅格、卡片与吸顶导航3.1 顶部导航实现吸顶、子菜单缩进与 hover 切换小米官网的顶部导航有一个典型特征页面往下滚动时导航条会固定在视口顶部这就是「吸顶效果」。实现方式不算复杂但有几个细节容易忽略——尤其是吸顶后导航的背景色、阴影和层级关系。/* 实现吸顶的核心position: fixed */ .site-header { position: fixed; top: 0; left: 0; width: 100%; z-index: 1000; background-color: #ffffff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); } /* 给 main 加一个顶部外边距补偿导航占位 */ .site-main { margin-top: 80px; /* 导航高度约 80px具体以实际为准 */ } /* 菜单 hover 状态橙色下划线 文字颜色加深 */ .nav-list li a { display: inline-block; padding: 0 15px; line-height: 80px; color: #333333; font-size: 16px; transition: color 0.2s ease; } .nav-list li a:hover { color: #ff6700; text-decoration: none; }这里有三处关键决策。第一position: fixed让导航脱离文档流不再参与页面占位所以main需要手动加margin-top把内容往下推——否则吸顶导航会盖住 banner 上半部分。第二z-index: 1000不是随便写的导航要盖住页面所有内容包括轮播图和商品卡片里的浮层元素所以层级必须比它们高。第三transition: color 0.2s ease是给 hover 变色加过渡不加的话颜色是瞬间切换的视觉上会很生硬。如果你还想要更丰富的子菜单效果——比如鼠标悬停「手机」弹出型号列表——可以用position: absolute配合opacity和visibility来做.dropdown-menu { position: absolute; top: 100%; left: 0; min-width: 180px; background: #ffffff; box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12); opacity: 0; visibility: hidden; transition: opacity 0.3s ease; } .nav-list li:hover .dropdown-menu { opacity: 1; visibility: visible; }子菜单的top: 100%是关键它会贴着父菜单项的底部边缘弹出不会把按钮顶到别的位置。至于为什么用visibility: hidden而不是display: none——因为display: none会阻止过渡动画触发菜单会「啪」地跳出来而不是「滑」出来。这个细节很多人容易翻车先记着。3.2 轮播图position 叠放、指示器与图片过渡的取舍轮播图是首页的视觉焦点也是最容易出 bug 的组件。这份资源的轮播实现采用的是「图片叠放」方案——所有 slide 叠加在同一个位置通过控制透明度来切换显示。这样做的好处是切换动画平滑且容器高度恒定不会出现内容跳动。.banner-carousel { position: relative; width: 1226px; height: 460px; margin: 0 auto; overflow: hidden; } .banner-slide { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 0.8s ease-in-out; } .banner-slide.active { opacity: 1; z-index: 2; } .banner-slide img { width: 100%; height: 100%; object-fit: cover; }每个 slide 都是绝对定位所以它们上下重叠在同一块区域。默认透明度为 0只有挂在active类上的那一帧显示出来。.active的z-index: 2是为了确保当前帧盖住其他帧——虽然 opacity 为 0 的元素不占视觉空间但为了保险起见显式设置层级更稳妥。对应地banner.js里的自动播放逻辑用一个定时器和索引计数器来切换active类// 获取所有 slide 和指示器点 const slides document.querySelectorAll(.banner-slide); const dots document.querySelectorAll(.banner-dot); let index 0; const interval 4000; // 4 秒切换一次 function goToSlide(nextIndex) { // 越界处理到达末尾回到第一张 nextIndex nextIndex slides.length ? 0 : nextIndex; slides[index].classList.remove(active); dots[index].classList.remove(active); index nextIndex; slides[index].classList.add(active); dots[index].classList.add(active); } // 定时器是轮播的心脏注意先清除再设定避免叠加 setInterval(function () { goToSlide(index 1); }, interval);interval参数控制自动播放速度4000ms 是官网常见节奏太快会让用户来不及看文案太慢又显得迟钝。手动切换的点击事件通常也要绑定到指示器dot上——常见的做法是循环给每个 dot 挂 click 事件点击后调用goToSlide(i)。这里要提一个有意思的取舍官网的 banner 是整张宽幅图片而这份资源的容器宽度是 1226px图片拉伸方式用了object-fit: cover。这意味着如果你的图片比例和容器不一致图片会被裁切一部分而不是被压扁。在实际做页面时最好先把图片尺寸裁好因为cover只会在固定尺寸下才能保证视觉完整否则每次加载都有可能裁到不同区域。3.3 商品卡片栅格Flex 间距计算与行内尺寸的玄学商品卡片区是首页信息密度最大的区域也是最考验栅格基本功的地方。小米官网的手机列表通常是 4 列布局每张卡片包含图片、名称、描述、价格和一个小标签。栅格间距如果算不对看起来最明显的问题就是「列数对了但左右留白不均」。.product-grid { display: flex; flex-wrap: wrap; margin: -20px 0 0 -20px; /* 用负 margin 抵消间距 */ } .card { width: calc(25% - 20px); margin: 20px 0 0 20px; background: #fafafa; border-radius: 8px; padding: 20px; box-sizing: border-box; text-align: center; }这里用的是一种比较巧妙的间距处理方式父容器设置负 margin子元素设置正 margin最终视觉上网格对齐到父容器边缘。具体逻辑是——如果.product-grid不设负 margin第一行第一张卡片的margin-top: 20px会把整行往下推同时左侧 20px 的 margin 会让卡片整体比标题往右缩进 20px。父容器用margin: -20px 0 0 -20px把这两个方向的外溢补齐视觉上就完全对齐了。而width: calc(25% - 20px)这个写法是给 margin 留出航向——如果不减去 20px四张卡片加上各自的 margin 会超过父容器宽度最后一行会被挤下去在 4 列布局里表现成「第三张卡片掉到第二行」。这个坑非常隐蔽因为只在特定宽度下才会暴露。我也会用gap替代负 margin 方案.product-grid { display: flex; flex-wrap: wrap; gap: 20px; } .card { width: calc(25% - 20px); margin: 0; }gap是 Flexbox 和 Grid 都支持的属性它自动处理元素之间的间距不会额外产生外溢如果你不需要兼容老旧浏览器gap其实更省心。但注意写calc(25% - 20px)时 25% 是相对父容器宽度的 25%而gap: 20px是行间距和列间距的合计。如果你同时用了gap和 margin间距会叠加视觉上比预期宽一倍。这种组合造成的「幽灵间距」是静态页面里最容易忽视的事。4. 避坑记录静态页面复刻时最容易翻车的五个场景4.1 图片加载后布局跳动现象页面打开后banner 下方的内容区域先挤在一起图片加载完突然往下弹开一段距离视觉上像是页面自己“跳”了一下。原因img标签在没有显式尺寸的情况下高度是 0只有等图片加载进来后才根据图片实际比例撑开容器。banner 区域和商品卡片区的高度变化导致后续所有内容跟着位移。解决给每张图片的容器设置固定宽高或者给img设置width和height属性。这份资源的做法是给.banner-carousel设了height: 460px卡片图片用aspect-ratio: 1锁定比例。我平时还会在 CSS 里给所有图片加一行img { display: block; }避免图片底部的间隙影响高度计算。4.2 hover 后子菜单闪烁甚至消失现象鼠标从父级菜单移向子菜单的过程中子菜单闪了一下就消失了必须非常快速且精准地划过才能保持打开。原因子菜单绝对定位后父菜单的hover区域和子菜单之间存在缝隙——这个缝隙通常是因为padding或top的值不够取整而产生的鼠标移经缝隙时就触发了mouseout。解决把子菜单的top: 100%改成贴近父菜单的top: 100%同时消除缝隙或者给父菜单的 hover 区域增加padding-bottom把缝隙填上。也可以让子菜单在 hover 时使用pointer-events: auto默认pointer-events: none这样鼠标在非打开状态下不容易误触打开状态下也能稳定保持。4.3 商品卡片文字底部不对齐现象一张卡片显示 3 行文字名称、描述、价格另一张卡片只有 2 行两张卡片高度一致但价格文字的下边线不在同一水平线。原因卡片内文本行数不同导致卡片底部空白高度不一致。Flex 布局下卡片默认是自动高度内容少的卡片会有多余空白。解决给卡片的文本区域设置min-height给描述行强制固定行数.card { display: flex; flex-direction: column; height: 100%; } .card-desc { flex: 1; line-height: 1.6; overflow: hidden; }关键是.card-desc的flex: 1它把多出来的高度分配给了描述部分让下方的价格区域被推到同一水平线上。这个方法比min-height更保险因为不需要猜文字内容多长。4.4 圆角卡片边缘出现锯齿感现象卡片设置了border-radius: 8px但在某些浏览器缩放下图片或背景的边缘会出现一行不自然的白边或锯齿。原因这是圆角裁剪精度和浏览器渲染引擎之间的兼容问题。尤其是当卡片内嵌图片、背景色是深色时半透明像素在圆角区域没有被完全裁剪干净。解决给有圆角的容器加transform: translateZ(0)或will-change: transform强制浏览器开启硬件加速来提升圆角裁剪精度。另外不要在同一层上同时使用border-radius和overflow: hidden时配合filter——filter: blur()这类效果会被循环应用到圆角边缘造成更明显的锯齿。4.5 点击按钮出现蓝色半透明遮罩现象在移动端或触屏设备上点击搜索按钮或菜单项时出现一个蓝色半透明方块过一会儿才消失。原因这是浏览器默认的:hover或-webkit-tap-highlight-color行为。在触屏设备上点击任何可点击元素都会有一个高亮闪烁来反馈触摸区域。解决在 reset 样式里加上一行* { -webkit-tap-highlight-color: transparent; }这是移动端开发的常规操作在静态页面里加上它也让你后续做响应式适配时少一个干扰项。要注意这行代码只对触屏设备生效桌面浏览器不受影响。5. 把页面改造成自己的移动端适配、组件抽离与性能收尾5.1 移动端适配三板斧viewport、媒体查询断点与 rem 替换这份资源默认是按桌面端设计的宽度 1226px。如果你希望页面在手机上也能看需要做三件事。第一确认index.html的head里已经包含了 viewport 标签——缺少这个标签手机浏览器会以 980px 的宽度渲染页面再缩放字体小到无法阅读。meta nameviewport contentwidthdevice-width, initial-scale1.0第二件事加一组媒体查询断点。常用的断点设置是 768px 以下切换为单列布局768px 到 1226px 之间保持双列或四列弹性。/* 平板两列布局 */ media (max-width: 768px) { .product-grid { gap: 12px; } .card { width: calc(50% - 12px); } } /* 手机单列布局 */ media (max-width: 480px) { .card { width: 100%; } .banner-carousel { height: 200px; } }媒体查询的核心是让卡片宽度从桌面端的calc(25% - 20px)逐步降级。断点值不是拍脑袋定的——480px 是常见手机逻辑宽度768px 是老 iPad 的竖屏宽度这两个值能覆盖绝大多数手持设备。第三件事考虑把固定像素字号改成相对单位。桌面端使用font-size: 16px没问题但移动端屏幕宽度变小后16px 在小屏上看起来偏大。常见做法是给html设一个基准字号再配上rem单位html { font-size: 16px; } media (max-width: 480px) { html { font-size: 14px; } } .nav-list li a { font-size: 1rem; }这样你把所有字号改成rem后改基准字号就能整体缩放文字不用逐条改 media query。这一步做完你的页面在手机上至少是能读的。如果你还想更精细可以把img的加载方式改成懒加载但静态页面没有大量图片时这不是必须项。5.2 组件抽离与 CSS 变量从单页改写到多页面复用把这份资源的 CSS 通读一遍你会发现有很多重复的段落——比如.card和.banner-slide里都写了border-radius、overflow: hidden、position: relative这类基础样式。单独维护一份静态页面没问题但如果你想新增一个「购物车页面」或「商品详情页」这些重复代码就会膨胀到难以维护。我常用的做法是先看哪些样式是多个组件共用的把它们以类名的形式抽出来/* 通用卡片容器 */ .panel { background: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); padding: 16px; } /* 两种页面方向 */ .product-card { font-family: Helvetica Neue, Arial, sans-serif; } .simple-card { font-family: PingFang SC, Microsoft YaHei, sans-serif; }然后再抽取设计变量。官网的主色是橙色#ff6700如果你需要换成自己的品牌色只在 CSS 变量里改一处即可不用满篇找十六进制颜色码:root { --primary-color: #ff6700; --text-color: #333333; --text-secondary: #999999; --card-radius: 8px; --container-width: 1226px; }之后所有用到这些值的地方都替换成var(--primary-color)。这样做的直接收益是改主题色时只动一处——两个类都不需要改你的视觉统一性也不会被破坏。5.3 验证与收尾用性能面板查重排、看字体加载页面改完后别急着关浏览器——打开开发者工具的 Performance 面板点一下录制再刷新页面看两条关键信息一是脚本执行耗时二是布局和绘制耗时。如果布局时间明显偏高多半是某些元素在加载后发生了重排比如图片没有预设宽高导致容器高度变化或者轮播切换时不断改opacity和z-index造成额外合成层。字体加载是另一个容易被忽视的细节。如果你的页面引入了自定义字体看 Network 面板里字体文件是不是在首屏就被下载了。字体未加载时浏览器会用 fallback 字体渲染文本字体加载完成后又会替换成正式字体——这会造成文字跳动视觉上非常明显。这份资源用的字体是系统默认字体栈没有这个问题但如果你给卡片标题加过特殊字体一定要检查这一步。最后一行一行对比你的页面和官网首页的视觉效果。不用在意像素级完全一致——这不现实但间距、字号、圆角这些维度的差异要在肉眼不可见的范围内。我第一次做静态页面复刻时卡片边距从 20px 调成 18px 就看了老半天一个像素的偏差往往会导致整体观感从「舒适」变成「局促」。从那以后我每次做完页面都会强制走一遍这个流程先验证结构再查间距差异最后用性能面板看有没有多余的重排操作——整条路走下来项目不会再出那种让你半夜抓头发的低级问题。希望帮到你。本文还有配套的精品资源点击获取