Claude Code 响应式设计实战:从 CSS 到 Playwright 验证
用 Claude Code 实作响应式设计:mobile-first CSS、clamp、网格、图片、导航、表格与 Playwright 截图检查。
先定义规则,再让 Claude Code 改页面
响应式设计不是把桌面页面缩小到手机上。真正可发布的响应式页面,要同时考虑屏幕宽度、触控区域、图片体积、导航优先级、表格可读性、广告位置和转化 CTA。如果只对 Claude Code 说“帮我做移动端适配”,它可能会把某一个宽度调得好看,但留下横向滚动、固定宽卡片、过大的首图、拥挤导航、购买按钮被内容压到很后面等问题。
更可靠的做法是给 Claude Code 一份小而明确的契约。mobile-first CSS 指的是先把窄屏作为默认样式,再为大屏添加增强。clamp() 是一个 CSS 函数,可以在一行里写最小值、理想值和最大值。container query 是容器查询,意思是组件根据父容器宽度变化,而不是只看整个浏览器宽度。把这些前提写清楚,Claude Code 生成的 diff 会更接近真实项目。
官方资料建议以 MDN 的 Responsive design、@container、clamp() 和 responsive images 为准。视觉验证使用 Playwright 的 Screenshots 和 Visual comparisons。Claude Code 的定位可以参考官方 overview 和 How Claude Code works:它会阅读代码库、编辑文件、运行命令并验证结果,所以我们也应该把验收条件写得具体。
相关基础可以搭配阅读 Claude Code 设计系统、Claude Code 无障碍实践 和 Claude Code Playwright 测试。
实作路线图
响应式改造最怕最后才补 CSS。更好的顺序是:让 Claude Code 先看现有页面,找出每个组件在哪些宽度会坏,再一起修改 CSS、图片和验证脚本。
flowchart LR
A["读取现有页面"] --> B["mobile-first 基础 CSS"]
B --> C["fluid grid 与 clamp()"]
C --> D["container query 组件"]
D --> E["响应式图片与表格"]
E --> F["Playwright 截图检查"]
可以直接给 Claude Code 这样的提示词:
请把现有的 /responsive-demo 页面改成响应式。
要求:
- 使用 mobile-first CSS。
- 在 320px、390px、768px、1024px、1440px 下不要出现横向滚动。
- 导航、卡片、价格表、文章 CTA 和页脚不能互相遮挡。
- 内容图片需要设置 width/height、srcset、sizes、loading 和合适的 alt。
- 优先使用 CSS Grid、clamp()、@container,不要先写 JS 判断宽度。
- 修改后用 Playwright 做截图和 overflow 检查。
不要:
- 改坏现有 URL、转化 CTA 链接或无障碍标题层级。
- 增加会造成整页横向滚动的大型固定 min-width。
这个提示词的重点不是文字多,而是把完成条件说清楚。之后你 review Claude Code 的结果时,就能直接对照这些条件。
可复制的 HTML 示例
下面的示例包含导航、首屏、图片、卡片、侧栏和比较表。把图片路径换成项目里的真实资源,就可以和下一节 CSS 一起运行。在 React、Astro、Vue 项目中也可以拆成组件,但初学时先用纯 HTML 更容易观察布局变化。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Responsive Demo</title>
<link rel="stylesheet" href="./responsive-demo.css" />
</head>
<body>
<header class="site-nav">
<a class="brand" href="/">ClaudeCodeLab</a>
<nav class="nav-links" aria-label="主导航">
<a href="/zh/blog/">文章</a>
<a href="/zh/products/">产品</a>
<a href="/zh/training/">咨询</a>
</nav>
</header>
<main class="page-shell">
<section class="hero">
<div>
<p class="eyebrow">Responsive Design</p>
<h1>先设计小屏,再让布局自然长大</h1>
<p class="lead">
把 mobile-first CSS、流动网格、响应式图片和 Playwright 验证放进同一个工作流。
</p>
<a class="primary-cta" href="/zh/products/">查看提示词模板</a>
</div>
<picture class="hero-media">
<source
type="image/avif"
srcset="/images/responsive-demo-640.avif 640w, /images/responsive-demo-1280.avif 1280w"
sizes="(width < 768px) 92vw, 40vw"
/>
<img
src="/images/responsive-demo-1280.jpg"
alt="手机和笔记本电脑显示同一个响应式页面"
width="1280"
height="900"
loading="eager"
decoding="async"
/>
</picture>
</section>
<div class="layout-grid">
<aside class="side-panel" aria-label="视口检查项">
<h2>需要验证的宽度</h2>
<ul>
<li>320px: 小屏手机</li>
<li>390px: 常见手机</li>
<li>768px: 平板</li>
<li>1024px 以上: 桌面</li>
</ul>
</aside>
<section class="cards" aria-label="改进卡片">
<article class="card featured">
<img src="/images/card-layout.jpg" alt="" width="720" height="480" loading="lazy" />
<div class="card-body">
<h2>让卡片响应容器宽度</h2>
<p>可复用组件不应该只看视口,也要根据所在容器的宽度调整密度。</p>
</div>
</article>
<article class="card">
<div class="card-body">
<h2>导航允许换行</h2>
<p>不要把所有桌面链接硬塞进手机一行,保留足够的点击区域。</p>
</div>
</article>
<article class="card">
<div class="card-body">
<h2>表格保留语义</h2>
<p>窄屏上把行变成卡片,并用 `data-label` 显示列名。</p>
</div>
</article>
</section>
</div>
<section class="comparison">
<h2>方案比较</h2>
<table class="responsive-table">
<thead>
<tr>
<th scope="col">项目</th>
<th scope="col">个人</th>
<th scope="col">团队</th>
</tr>
</thead>
<tbody>
<tr>
<td data-label="项目">目标</td>
<td data-label="个人">学习与小范围改进</td>
<td data-label="团队">统一 review 标准</td>
</tr>
<tr>
<td data-label="项目">验证</td>
<td data-label="个人">本地 Playwright</td>
<td data-label="团队">CI 截图检查</td>
</tr>
</tbody>
</table>
</section>
</main>
</body>
</html>
mobile-first CSS、流动网格与 clamp
CSS 从窄屏开始写,大屏再用 media query 增强。这样不会不断用移动端规则去覆盖桌面端规则。卡片列数交给 repeat(auto-fit, minmax(...)),标题和间距用 clamp(),特色卡片则用容器查询判断父容器是否足够宽。
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #172033;
background: #f7f8fb;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
img {
display: block;
max-width: 100%;
height: auto;
}
.site-nav,
.page-shell {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.site-nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
padding-block: 1rem;
}
.brand,
.nav-links a,
.primary-cta {
min-height: 44px;
display: inline-flex;
align-items: center;
}
.nav-links {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.nav-links a {
padding-inline: 0.75rem;
color: inherit;
text-decoration: none;
}
.page-shell {
padding-block: clamp(1rem, 4vw, 3rem);
}
.hero {
display: grid;
gap: clamp(1rem, 4vw, 2.5rem);
align-items: center;
}
.eyebrow {
color: #0f766e;
font-weight: 700;
}
.hero h1 {
max-width: 11ch;
margin: 0;
font-size: clamp(2.25rem, 10vw, 5rem);
line-height: 1.02;
}
.lead {
max-width: 62ch;
font-size: clamp(1rem, 2vw, 1.25rem);
line-height: 1.8;
}
.primary-cta {
width: fit-content;
border-radius: 0.5rem;
padding-inline: 1rem;
background: #172033;
color: white;
text-decoration: none;
font-weight: 700;
}
.hero-media img,
.card {
border-radius: 0.75rem;
box-shadow: 0 18px 50px rgb(15 23 42 / 0.12);
}
.layout-grid {
display: grid;
gap: clamp(1rem, 3vw, 2rem);
margin-block-start: 2rem;
}
.side-panel,
.card,
.comparison {
background: white;
border: 1px solid #dbe3ef;
border-radius: 0.75rem;
}
.side-panel {
padding: 1rem;
}
.cards {
container: cards / inline-size;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1rem;
}
.card {
overflow: hidden;
}
.card-body {
display: grid;
gap: 0.75rem;
padding: 1rem;
}
.card h2,
.comparison h2 {
margin: 0;
font-size: clamp(1.25rem, 3vw, 1.75rem);
}
@container cards (width >= 42rem) {
.card.featured {
grid-column: span 2;
display: grid;
grid-template-columns: minmax(14rem, 0.8fr) 1fr;
}
.card.featured img {
height: 100%;
object-fit: cover;
}
}
.comparison {
margin-block-start: 2rem;
padding: 1rem;
overflow-x: auto;
}
.responsive-table {
width: 100%;
border-collapse: collapse;
}
.responsive-table th,
.responsive-table td {
padding: 0.875rem;
border-block-end: 1px solid #dbe3ef;
text-align: left;
}
@media (width < 48rem) {
.responsive-table thead {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.responsive-table,
.responsive-table tbody,
.responsive-table tr,
.responsive-table td {
display: block;
width: 100%;
}
.responsive-table tr {
border: 1px solid #dbe3ef;
border-radius: 0.5rem;
margin-block: 0.75rem;
overflow: hidden;
}
.responsive-table td {
display: grid;
grid-template-columns: minmax(7rem, 40%) 1fr;
gap: 1rem;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: 700;
color: #526071;
}
}
@media (width >= 64rem) {
.hero {
grid-template-columns: minmax(0, 1.1fr) minmax(18rem, 0.9fr);
}
.layout-grid {
grid-template-columns: 16rem minmax(0, 1fr);
}
.side-panel {
position: sticky;
top: 1rem;
align-self: start;
}
}
这里最重要的不是颜色,而是避免大固定宽度。width: min(100% - 2rem, 72rem) 让小屏保留边距,大屏限制阅读宽度。minmax(min(100%, 18rem), 1fr) 则防止卡片最小宽度撑破容器。
图片、导航、卡片、表格的判断标准
把组件级规则交给 Claude Code,比一句“改成响应式”更有效。
| 组件 | 常见问题 | 给 Claude Code 的要求 |
|---|---|---|
| 导航 | 桌面链接硬挤到手机一行 | 允许换行,保留点击区域和 aria-label |
| 卡片 | width: 320px 或大 min-width 造成 overflow | 使用 auto-fit、minmax()、container query |
| 图片 | 手机也加载同一张大图 | 添加 srcset、sizes、width、height、loading、alt |
| 表格 | 多列挤压导致无法阅读 | 选择横向滚动或行卡片,并保留 data-label |
| CTA | 收益入口被图片或广告压住 | 检查移动端第一屏下方和文章末尾 |
并不是所有小屏都需要汉堡菜单。只有三个链接时,换行往往更直接。表格也一样,价格对比适合横向比较,而工单列表、客户记录更适合变成行卡片。让 Claude Code 保留用户任务,而不是机械保留桌面形状。
至少要测试的用例
第一个用例是 SaaS 仪表盘。侧栏、筛选、KPI 卡片、图表和数据表会同时出现。移动端不能只是全部堆叠,而要优先展示关键指标,折叠次要筛选,并把详情表变成可读卡片。
第二个用例是博客或内容站。正文宽度、目录、广告、相关文章、免费 PDF 注册 CTA 会争夺空间。这里要重点检查行长、图片懒加载、代码块横向滚动,以及 CTA 是否被长代码示例淹没。
第三个用例是电商或课程销售页。商品卡、价格比较、购买按钮和 FAQ 都影响收入。如果价格和购买按钮在手机上掉得太后,转化会受影响,所以要把 CTA 顺序写进 Claude Code 的验收条件。
第四个用例是内部管理页面。日常用户更在意搜索、筛选、键盘操作和表格可读性。响应式改造应该保护原有工作顺序,而不是追求炫目的视觉效果。
用 Playwright 做截图和横向滚动检查
不要只靠肉眼看一次浏览器。Playwright 可以在代表性宽度下检查关键元素是否可见、页面是否出现横向滚动,并保存截图用于 review。
import { expect, test } from "@playwright/test";
const baseUrl = process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000";
const viewports = [
{ name: "mobile-320", width: 320, height: 740 },
{ name: "mobile-390", width: 390, height: 844 },
{ name: "tablet-768", width: 768, height: 1024 },
{ name: "desktop-1440", width: 1440, height: 1000 },
];
for (const viewport of viewports) {
test(`responsive demo has no horizontal overflow at ${viewport.name}`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.goto(`${baseUrl}/responsive-demo`);
await expect(page.getByRole("navigation", { name: "主导航" })).toBeVisible();
await expect(page.getByRole("link", { name: "查看提示词模板" })).toBeVisible();
const hasHorizontalOverflow = await page.evaluate(() => {
return document.documentElement.scrollWidth > document.documentElement.clientWidth;
});
expect(hasHorizontalOverflow).toBe(false);
await expect(page).toHaveScreenshot(`responsive-${viewport.name}.png`, {
fullPage: true,
maxDiffPixels: 300,
});
});
}
第一次执行会生成基准截图。只有设计有意变化时,才用 npx playwright test --update-snapshots 更新。截图会受到操作系统、字体渲染、GPU 和 headless 设置影响,所以团队最好在同一种 CI 环境里生成和比较。
常见坑与失败例
最大的问题是桌面优先 CSS。先写桌面,再不断用移动端规则覆盖,短期能用,长期会让 cascade 难以理解。让 Claude Code 把窄屏样式放回基础规则,再为大屏添加布局。
第二个坑是缺少 <meta name="viewport">。没有它,移动浏览器可能按虚拟桌面宽度渲染,导致你以为 CSS 正确,实机却不对。
第三个坑是卡片、表格、图片或第三方嵌入里藏着固定宽度。min-width: 960px 在桌面看不出来,却会破坏手机。表格必须横向滚动时,只给表格外层加 overflow-x: auto,不要让整页滚动。
第四个坑是只写 srcset 不写 sizes。浏览器需要知道候选图片和预期显示宽度,才能选择合适资源。让 Claude Code 一起检查 srcset、sizes、width、height 和 alt。
第五个坑是过度相信截图测试。截图适合抓视觉差异,但广告、日期、动画和第三方组件会带来噪声。要同时保留 DOM 断言,例如 overflow、CTA 可见性和 navigation landmark。
把收益路径一起纳入响应式改造
响应式设计也应该服务业务目标。对 ClaudeCodeLab 这样的内容站来说,读者在手机上也要能找到免费 PDF、Gumroad 产品和咨询入口。想建立可复用工作流,可以看 Claude Code 产品与提示词模板;如果团队想用真实页面一起梳理 review 规则和 Playwright 验证,可以看 Claude Code 培训与咨询。
给 Claude Code 写 brief 时,把转化路径写进完成条件。一个看起来干净、但隐藏购买按钮或咨询链接的页面,不算完成。
总结
Claude Code 做响应式设计的实务流程是:先定义 mobile-first 契约,再实现流动网格,用 clamp() 控制字号和间距,用 container query 处理可复用组件,认真处理图片、导航和表格,最后用 Playwright 截图和 overflow 断言验证。
我把本文的 demo 模式在本地 320px、390px、768px、1440px 下测试过。导航会换行而不是溢出,卡片在手机上变成一列,表格变成可读的行卡片,Playwright 的横向滚动断言也通过。Masa 的实际感受是:让 Claude Code 同时担任实现者和审查者,专门怀疑固定宽度、图片提示、CTA 位置、表格行为和截图差异,发布前返工会少很多。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code权限安全阶梯:逐步放开访问而不失控
从只读到有限编辑、验证命令和部署检查的 Claude Code 权限升级流程。
Claude Code 小PR证据包:让小改动真正可审查
用差异、验证命令、公开URL、CTA路径和回滚说明,把Claude Code的小PR变得可审查。
Claude Code 提交前 Review Gate:同时检查差异、测试、公开 URL 和 CTA
提交前用 Claude Code 审查差异范围、build、公开 URL、Gumroad 链接、咨询 CTA、缺少测试和无关文件。