响应式设计:一套代码适配手机和电脑
白物集上线后第一个被吐槽的不是内容,是排版——手机上看字号太小,图片超出屏幕,导航栏挤成一团。这不是内容问题,是布局没跟上设备。
没有响应式之前的日子
2010 年代中期的网站大多固定宽度的设计——960px、1140px,写死在 CSS 里。在 27 寸显示器上打开正好,在 iPhone 上浏览就得双指缩放、左右滑动。2011 年 Ethan Marcotte 提出了 Responsive Web Design 这个概念,核心三要素后来成了所有前端框架的基础:流式栅格(fluid grid)、弹性图片(flexible images)、媒体查询(media queries)。
起点:viewport meta 标签
白物集 BaseLayout.astro 的第一行 head 里有一个被很多人忽略的标签:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
没有这一行,iPhone 会把 1080px 宽的页面缩到 375px 显示——所有字变成蚂蚁。加上它,浏览器才按真实设备宽度渲染。这是响应式的起点,也是白物集 HTML 里最不可能被删掉的 92 个字符。
Tailwind 的断点体系
白物集使用 daisyUI(Tailwind CSS 组件库),整个 CSS 框架建立在 5 个断点之上:
| 断点 | 最小宽度 | 典型设备 |
|---|---|---|
sm |
640px | 横屏手机、小平板 |
md |
768px | iPad |
lg |
1024px | 笔记本 |
xl |
1280px | 桌面显示器 |
2xl |
1536px | 大屏 |
实际使用时,白物集最常用的是 sm:flex 和 md:grid-cols-2 这种组合前缀。以科技频道页面的文章网格为例:
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5" id="article-grid">
一行代码解决三个布局:手机上 1 列、平板上 2 列、桌面 3 列。不写媒体查询,不写 JavaScript 检测,纯 CSS 在 build 时编译成对应的 @media 规则。这个 id="article-grid" 在后面关联了 JavaScript 的搜索和分类筛选逻辑,但布局本身完全由 CSS 控制——前端团队常犯的错误是用 JS 去动态计算列数,而 Tailwind 的做法是用编译期生成的媒体查询,零运行时开销。
导航栏的手机端适配
白物集的导航栏 NavBar.astro 里有一段典型的适配逻辑:
<div class="navbar-center hidden sm:flex">
<ul class="menu menu-horizontal px-1 gap-1 text-sm">
{items.map((item) => (
<li><a href={item.href} class="btn btn-ghost btn-sm">{item.label}</a></li>
))}
</ul>
</div>
关键在 hidden sm:flex:屏幕宽度 < 640px 时导航链接隐藏,≥ 640px 时显示。手机上空间有限,只保留 logo 和主题切换按钮。简单的显示/隐藏组合,比手风琴菜单省 80% 的 JS 代码。
媒体查询的二次修正
Tailwind 的 utility class 能覆盖 95% 的场景,但总有些边角需要自己写 @media。白物集的 global.css 里有一段自定义栅格修正:
.card-grid {
display: grid;
gap: var(--space-4);
grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
}
.card-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.card-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) {
.card-grid.cols-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 700px) {
.card-grid { grid-template-columns: 1fr; }
.card-grid.cols-2,
.card-grid.cols-3 { grid-template-columns: 1fr; }
}
这是一个先设「主流」再退化到「小屏」的策略——用 auto-fill + minmax 做自适应的基准,再用媒体查询覆盖特定组合。900px 是从 3 列退到 2 列的拐点,700px 是全部退到单列的底线。这两个阈值不是猜的——在白物集测试中,900px 以下的三列卡每张只剩 240px 宽,字号和图片已经没法看了。
移动端的整体收敛
global.css 最后有一段统一的 Mobile 区块,把手机上的字号、间距、布局一并收拢:
@media (max-width: 768px) {
.wrapper { padding: 0 var(--space-3); }
h1 { font-size: var(--font-size-2xl); }
.section-title { font-size: var(--font-size-xl); }
.card-grid { gap: var(--space-3); }
.card { padding: var(--space-4); }
.card .meta { font-size: 0.75rem; gap: var(--space-2); }
.article-nav { flex-direction: column; gap: var(--space-2); }
.article-nav a { max-width: 100%; }
}
几个细节值得注意:
- 间距收窄:卡片的内边距从
var(--space-5)缩到var(--space-4),每个方向省 4px,一个屏幕能多显示半行内容。 - 字体降级:h1 从桌面版降两档,标题在手机上不会顶到屏幕边缘。
- 导航变列:文章的前一篇/后一篇导航从水平并排放到垂直,避免两行文字挤在一行断裂。
一个容易踩的坑:overflow-x
文章列表页的 .nav-links 在手机上可能出现标签溢出的问题。白物集的修复方案:
@media (max-width: 768px) {
.nav-links {
gap: 2px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.nav-links a {
font-size: 12px;
padding: 6px 8px;
white-space: nowrap;
}
}
overflow-x: auto 让标签行在空间不够时横向滚动而不是折行断掉,white-space: nowrap 防止链接文字被截断。-webkit-overflow-scrolling: touch 是 iOS 上让滚动有惯性的关键——不加的话手指一抬滑动立刻停住,体验很差。
导航栏的响应式 logo
白物集导航栏的 Logo 是一只 SVG 绘制的白物集图标,在代码中同时设置了固定宽度和 CSS 变量约束:
<svg width="20" height="20" viewBox="0 0 512 512" fill="none" class="shrink-0 theme-aware-logo">
viewBox="0 0 512 512" 配合 width="20" 让图标在保持比例的前提下指定了显示尺寸。这个 20x20 的 SVG 不会因为屏幕分辨率差异而失真,也比 png 图标节省一个 HTTP 请求。shrink-0 防止在 flex 容器中被压缩——导航栏空间紧张时,SVG 图标保持 20px 宽,不参与弹性收缩。这套做法对白物集首页的大 Logo 也适用,首页用 max-width:400px; height:auto 确保全屏和窄屏下都不会变形或溢出。
响应式不是「适配手机」
做了白物集之后的一个体会:响应式不只是让网站在手机上能用。同一个页面在不同设备上的信息优先级不同——手机上用户想看的是核心内容(文章标题、正文),而不是侧栏的标签云和归档。白物集在手机端隐藏了分享按钮列的 flex-wrap 副作用、把 search-sort-bar 从水平改为垂直排列,每一处都在对用户说「我知道你在用什么设备」。基类 wrapper 的内边距也从桌面端的 var(--space-6) 缩到移动端的 var(--space-3),相当于每个页面在手机上多了 48px 的可用宽度。
下一篇会讲响应式之后的自然延伸——JavaScript 入门。一个连 CSS 都懂得适配不同设备的网站,它的交互逻辑也应该灵活起来。
关键词:响应式设计、媒体查询、Tailwind CSS 断点、viewport、白物集