responsive-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseResponsive Patterns
响应式设计模式
Modern responsive design patterns using Container Queries, fluid typography, and mobile-first strategies for React applications (2026 best practices).
适用于React应用的现代化响应式设计模式,采用Container Queries、流体排版和移动优先策略(2026最佳实践)。
Overview
概述
- Building reusable components that adapt to their container
- Implementing fluid typography that scales smoothly
- Creating responsive layouts without media query overload
- Building design system components for multiple contexts
- Optimizing for variable container sizes (sidebars, modals, grids)
- 构建可适配容器的可复用组件
- 实现平滑缩放的流体排版
- 无需大量媒体查询即可创建响应式布局
- 为多场景构建设计系统组件
- 针对可变容器尺寸(侧边栏、模态框、网格)进行优化
Core Concepts
核心概念
Container Queries vs Media Queries
容器查询 vs 媒体查询
| Feature | Media Queries | Container Queries |
|---|---|---|
| Responds to | Viewport size | Container size |
| Component reuse | Context-dependent | Truly portable |
| Browser support | Universal | Baseline 2023+ |
| Use case | Page layouts | Component layouts |
| 特性 | 媒体查询 | 容器查询 |
|---|---|---|
| 响应依据 | 视口尺寸 | 容器尺寸 |
| 组件复用性 | 依赖上下文 | 真正可移植 |
| 浏览器支持 | 全兼容 | 2023年基线版本+ |
| 使用场景 | 页面布局 | 组件布局 |
CSS Patterns
CSS设计模式
1. Container Query Basics
1. 容器查询基础
css
/* Define a query container */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Style based on container width */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}css
/* 定义查询容器 */
.card-container {
container-type: inline-size;
container-name: card;
}
/* 根据容器宽度设置样式 */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}2. Container Query Units (cqi, cqb)
2. 容器查询单位(cqi, cqb)
css
/* Use cqi (container query inline) over cqw */
.card-title {
/* 5% of container's inline size */
font-size: clamp(1rem, 5cqi, 2rem);
}
.card-content {
/* Responsive padding based on container */
padding: 2cqi;
}
/* cqb for block dimension (height-aware containers) */
.sidebar-item {
height: 10cqb;
}css
/* 使用cqi(容器查询内联单位)替代cqw */
.card-title {
/* 容器内联尺寸的5% */
font-size: clamp(1rem, 5cqi, 2rem);
}
.card-content {
/* 基于容器的响应式内边距 */
padding: 2cqi;
}
/* cqb用于块级维度(感知高度的容器) */
.sidebar-item {
height: 10cqb;
}3. Fluid Typography with clamp()
3. 结合clamp()的流体排版
css
/* Accessible fluid typography */
:root {
/* Base font respects user preferences (rem) */
--font-size-base: 1rem;
/* Fluid scale with min/max bounds */
--font-size-sm: clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
--font-size-md: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-lg: clamp(1.25rem, 1rem + 1vw, 2rem);
--font-size-xl: clamp(1.5rem, 1rem + 2vw, 3rem);
--font-size-2xl: clamp(2rem, 1rem + 3vw, 4rem);
}
h1 { font-size: var(--font-size-2xl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
p { font-size: var(--font-size-md); }
small { font-size: var(--font-size-sm); }css
/* 符合无障碍标准的流体排版 */
:root {
/* 基础字体尊重用户偏好(rem单位) */
--font-size-base: 1rem;
/* 带最小/最大限制的流体缩放 */
--font-size-sm: clamp(0.875rem, 0.8rem + 0.25vw, 1rem);
--font-size-md: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-lg: clamp(1.25rem, 1rem + 1vw, 2rem);
--font-size-xl: clamp(1.5rem, 1rem + 2vw, 3rem);
--font-size-2xl: clamp(2rem, 1rem + 3vw, 4rem);
}
h1 { font-size: var(--font-size-2xl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
p { font-size: var(--font-size-md); }
small { font-size: var(--font-size-sm); }4. Container-Based Fluid Typography
4. 基于容器的流体排版
css
/* For component-scoped fluid text */
.widget {
container-type: inline-size;
}
.widget-title {
/* Fluid within container, respecting user rem */
font-size: clamp(1rem, 0.5rem + 5cqi, 2rem);
}
.widget-body {
font-size: clamp(0.875rem, 0.5rem + 3cqi, 1.125rem);
}css
/* 组件作用域内的流体文本 */
.widget {
container-type: inline-size;
}
.widget-title {
/* 在容器内流体缩放,同时尊重用户的rem设置 */
font-size: clamp(1rem, 0.5rem + 5cqi, 2rem);
}
.widget-body {
font-size: clamp(0.875rem, 0.5rem + 3cqi, 1.125rem);
}5. Mobile-First Breakpoints
5. 移动优先断点
css
/* Mobile-first: start small, add complexity */
.layout {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Tablet and up */
@media (min-width: 768px) {
.layout {
flex-direction: row;
}
}
/* Desktop */
@media (min-width: 1024px) {
.layout {
max-width: 1200px;
margin-inline: auto;
}
}css
/* 移动优先:从简单布局开始,逐步增加复杂度 */
.layout {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* 平板及以上设备 */
@media (min-width: 768px) {
.layout {
flex-direction: row;
}
}
/* 桌面设备 */
@media (min-width: 1024px) {
.layout {
max-width: 1200px;
margin-inline: auto;
}
}6. CSS Grid Responsive Patterns
6. CSS网格响应式模式
css
/* Auto-fit grid (fills available space) */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
/* Auto-fill grid (maintains minimum columns) */
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 1rem;
}
/* Subgrid for nested alignment */
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}css
/* 自动适配网格(填充可用空间) */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
/* 自动填充网格(保持最小列数) */
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 1rem;
}
/* 用于嵌套对齐的子网格 */
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}7. Container Scroll-Queries (Chrome 126+)
7. 容器滚动查询(Chrome 126+)
css
/* Query based on scroll state */
.scroll-container {
container-type: scroll-state;
container-name: scroller;
}
@container scroller scroll-state(scrollable: top) {
.scroll-indicator-top {
opacity: 0;
}
}
@container scroller scroll-state(scrollable: bottom) {
.scroll-indicator-bottom {
opacity: 0;
}
}css
/* 根据滚动状态查询 */
.scroll-container {
container-type: scroll-state;
container-name: scroller;
}
@container scroller scroll-state(scrollable: top) {
.scroll-indicator-top {
opacity: 0;
}
}
@container scroller scroll-state(scrollable: bottom) {
.scroll-indicator-bottom {
opacity: 0;
}
}React Patterns
React设计模式
Responsive Component with Container Queries
结合容器查询的响应式组件
tsx
import { cn } from '@/lib/utils';
interface CardProps {
title: string;
description: string;
image: string;
}
export function ResponsiveCard({ title, description, image }: CardProps) {
return (
<div className="@container">
<article className={cn(
"flex flex-col gap-4",
"@md:flex-row @md:gap-6" // Container query breakpoints
)}>
<img
src={image}
alt=""
className="w-full @md:w-48 aspect-video object-cover rounded-lg"
/>
<div className="flex flex-col gap-2">
<h3 className="text-[clamp(1rem,0.5rem+3cqi,1.5rem)] font-semibold">
{title}
</h3>
<p className="text-[clamp(0.875rem,0.5rem+2cqi,1rem)] text-muted-foreground">
{description}
</p>
</div>
</article>
</div>
);
}tsx
import { cn } from '@/lib/utils';
interface CardProps {
title: string;
description: string;
image: string;
}
export function ResponsiveCard({ title, description, image }: CardProps) {
return (
<div className="@container">
<article className={cn(
"flex flex-col gap-4",
"@md:flex-row @md:gap-6" // 容器查询断点
)}>
<img
src={image}
alt=""
className="w-full @md:w-48 aspect-video object-cover rounded-lg"
/>
<div className="flex flex-col gap-2">
<h3 className="text-[clamp(1rem,0.5rem+3cqi,1.5rem)] font-semibold">
{title}
</h3>
<p className="text-[clamp(0.875rem,0.5rem+2cqi,1rem)] text-muted-foreground">
{description}
</p>
</div>
</article>
</div>
);
}Tailwind CSS Container Queries
Tailwind CSS容器查询
tsx
// @container enables container query variants (@sm, @md, @lg, etc.)
<div className="@container">
<div className="flex flex-col @lg:flex-row @xl:gap-8">
<div className="@sm:p-4 @md:p-6 @lg:p-8">
Content adapts to container
</div>
</div>
</div>tsx
// @container 启用容器查询变体(@sm, @md, @lg等)
<div className="@container">
<div className="flex flex-col @lg:flex-row @xl:gap-8">
<div className="@sm:p-4 @md:p-6 @lg:p-8">
内容将适配容器尺寸
</div>
</div>
</div>useContainerQuery Hook
useContainerQuery钩子
tsx
import { useRef, useState, useEffect } from 'react';
function useContainerQuery(breakpoint: number) {
const ref = useRef<HTMLDivElement>(null);
const [isAbove, setIsAbove] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new ResizeObserver(([entry]) => {
setIsAbove(entry.contentRect.width >= breakpoint);
});
observer.observe(element);
return () => observer.disconnect();
}, [breakpoint]);
return [ref, isAbove] as const;
}
// Usage
function AdaptiveCard() {
const [containerRef, isWide] = useContainerQuery(400);
return (
<div ref={containerRef}>
{isWide ? <HorizontalLayout /> : <VerticalLayout />}
</div>
);
}tsx
import { useRef, useState, useEffect } from 'react';
function useContainerQuery(breakpoint: number) {
const ref = useRef<HTMLDivElement>(null);
const [isAbove, setIsAbove] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new ResizeObserver(([entry]) => {
setIsAbove(entry.contentRect.width >= breakpoint);
});
observer.observe(element);
return () => observer.disconnect();
}, [breakpoint]);
return [ref, isAbove] as const;
}
// 使用示例
function AdaptiveCard() {
const [containerRef, isWide] = useContainerQuery(400);
return (
<div ref={containerRef}>
{isWide ? <HorizontalLayout /> : <VerticalLayout />}
</div>
);
}Responsive Images Pattern
响应式图片模式
tsx
function ResponsiveImage({
src,
alt,
sizes = "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
}: {
src: string;
alt: string;
sizes?: string;
}) {
return (
<picture>
{/* Art direction with different crops */}
<source
media="(max-width: 640px)"
srcSet={`${src}?w=640&aspect=1:1`}
/>
<source
media="(max-width: 1024px)"
srcSet={`${src}?w=800&aspect=4:3`}
/>
<img
src={`${src}?w=1200`}
alt={alt}
sizes={sizes}
loading="lazy"
decoding="async"
className="w-full h-auto object-cover"
/>
</picture>
);
}tsx
function ResponsiveImage({
src,
alt,
sizes = "(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
}: {
src: string;
alt: string;
sizes?: string;
}) {
return (
<picture>
{/* 按场景调整图片裁剪 */}
<source
media="(max-width: 640px)"
srcSet={`${src}?w=640&aspect=1:1`}
/>
<source
media="(max-width: 1024px)"
srcSet={`${src}?w=800&aspect=4:3`}
/>
<img
src={`${src}?w=1200`}
alt={alt}
sizes={sizes}
loading="lazy"
decoding="async"
className="w-full h-auto object-cover"
/>
</picture>
);
}Accessibility Considerations
无障碍设计注意事项
css
/* IMPORTANT: Always include rem in fluid typography */
/* This ensures user font preferences are respected */
/* ❌ WRONG: Viewport-only ignores user preferences */
font-size: 5vw;
/* ✅ CORRECT: Include rem to respect user settings */
font-size: clamp(1rem, 0.5rem + 2vw, 2rem);
/* User zooming must still work */
@media (min-width: 768px) {
/* Use em/rem, not px, for breakpoints in ideal world */
/* (browsers still use px, but consider user zoom) */
}css
/* 重要提示:流体排版中务必包含rem单位 */
/* 这能确保尊重用户的字体偏好设置 */
/* ❌ 错误:仅使用视口单位会忽略用户偏好 */
font-size: 5vw;
/* ✅ 正确:包含rem以尊重用户设置 */
font-size: clamp(1rem, 0.5rem + 2vw, 2rem);
/* 用户缩放功能必须正常工作 */
@media (min-width: 768px) {
/* 理想情况下使用em/rem,而非px作为断点 */
/* (浏览器仍支持px,但需考虑用户缩放) */
}Anti-Patterns (FORBIDDEN)
反模式(禁止使用)
css
/* ❌ NEVER: Use only viewport units for text */
.title {
font-size: 5vw; /* Ignores user font preferences! */
}
/* ❌ NEVER: Use cqw/cqh (use cqi/cqb instead) */
.card {
padding: 5cqw; /* cqw = container width, not logical */
}
/* ✅ CORRECT: Use logical units */
.card {
padding: 5cqi; /* Container inline = logical direction */
}
/* ❌ NEVER: Container queries without container-type */
@container (min-width: 400px) {
/* Won't work without container-type on parent! */
}
/* ❌ NEVER: Desktop-first media queries */
.element {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 768px) {
.element {
grid-template-columns: 1fr; /* Overriding = more CSS */
}
}
/* ❌ NEVER: Fixed pixel breakpoints for text */
@media (min-width: 768px) {
body { font-size: 18px; } /* Use rem! */
}
/* ❌ NEVER: Over-nesting container queries */
@container a {
@container b {
@container c {
/* Too complex, reconsider architecture */
}
}
}css
/* ❌ 绝对禁止:仅使用视口单位设置文本大小 */
.title {
font-size: 5vw; /* 忽略了用户的字体偏好设置! */
}
/* ❌ 绝对禁止:使用cqw/cqh(改用cqi/cqb) */
.card {
padding: 5cqw; /* cqw = 容器宽度,不符合逻辑方向 */
}
/* ✅ 正确:使用逻辑单位 */
.card {
padding: 5cqi; /* 容器内边距 = 逻辑方向 */
}
/* ❌ 绝对禁止:未设置container-type就使用容器查询 */
@container (min-width: 400px) {
/* 父元素未设置container-type则无法生效! */
}
/* ❌ 绝对禁止:桌面优先的媒体查询 */
.element {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 768px) {
.element {
grid-template-columns: 1fr; /* 覆盖原有样式 = 更多CSS代码 */
}
}
/* ❌ 绝对禁止:为文本使用固定像素断点 */
@media (min-width: 768px) {
body { font-size: 18px; } /* 请使用rem! */
}
/* ❌ 绝对禁止:过度嵌套容器查询 */
@container a {
@container b {
@container c {
/* 过于复杂,请重新考虑架构设计 */
}
}
}Browser Support
浏览器支持
| Feature | Chrome | Safari | Firefox | Edge |
|---|---|---|---|---|
| Container Size Queries | 105+ | 16+ | 110+ | 105+ |
| Container Style Queries | 111+ | ❌ | ❌ | 111+ |
| Container Scroll-State | 126+ | ❌ | ❌ | 126+ |
| cqi/cqb units | 105+ | 16+ | 110+ | 105+ |
| clamp() | 79+ | 13.1+ | 75+ | 79+ |
| Subgrid | 117+ | 16+ | 71+ | 117+ |
| 特性 | Chrome | Safari | Firefox | Edge |
|---|---|---|---|---|
| 容器尺寸查询 | 105+ | 16+ | 110+ | 105+ |
| 容器样式查询 | 111+ | ❌ | ❌ | 111+ |
| 容器滚动状态查询 | 126+ | ❌ | ❌ | 126+ |
| cqi/cqb单位 | 105+ | 16+ | 110+ | 105+ |
| clamp() | 79+ | 13.1+ | 75+ | 79+ |
| 子网格 | 117+ | 16+ | 71+ | 117+ |
Rules
规范
Each category has individual rule files in loaded on-demand:
rules/| Category | Rule | Impact | Key Pattern |
|---|---|---|---|
| PWA | | HIGH | Workbox caching strategies, VitePWA, update management |
| PWA | | HIGH | Offline hooks, background sync, install prompts |
| Animation | | HIGH | Motion presets, AnimatePresence, View Transitions |
| Animation | | MEDIUM | CSS scroll-driven animations, parallax, progressive enhancement |
Total: 4 rules across 2 categories
每个分类的独立规范文件位于目录,按需加载:
rules/| 分类 | 规范 | 影响程度 | 核心模式 |
|---|---|---|---|
| PWA | | 高 | Workbox缓存策略、VitePWA、更新管理 |
| PWA | | 高 | 离线钩子、后台同步、安装提示 |
| 动画 | | 高 | 动效预设、AnimatePresence、视图过渡 |
| 动画 | | 中 | CSS滚动驱动动画、视差滚动、渐进式增强 |
总计:2个分类下的4条规范
Key Decisions
关键决策
| Decision | Option A | Option B | Recommendation |
|---|---|---|---|
| Query type | Media queries | Container queries | Container for components, Media for layout |
| Container units | cqw/cqh | cqi/cqb | cqi/cqb (logical, i18n-ready) |
| Fluid type base | vw only | rem + vw | rem + vw (accessibility) |
| Mobile-first | Yes | Desktop-first | Mobile-first (less CSS, progressive) |
| Grid pattern | auto-fit | auto-fill | auto-fit for cards, auto-fill for icons |
| 决策项 | 选项A | 选项B | 推荐方案 |
|---|---|---|---|
| 查询类型 | 媒体查询 | 容器查询 | 容器查询用于组件,媒体查询用于页面布局 |
| 容器单位 | cqw/cqh | cqi/cqb | cqi/cqb(符合逻辑方向,支持国际化) |
| 流体排版基准 | 仅vw | rem + vw | rem + vw(符合无障碍设计) |
| 优先策略 | 移动优先 | 桌面优先 | 移动优先(更少CSS代码,渐进式增强) |
| 网格模式 | auto-fit | auto-fill | 卡片布局用auto-fit,图标布局用auto-fill |
Related Skills
相关技能
- - Building responsive design systems
design-system-starter - - CLS, responsive images, and image optimization
performance - - RTL/LTR responsive considerations
i18n-date-patterns
- - 构建响应式设计系统
design-system-starter - - CLS优化、响应式图片、图片性能优化
performance - - 从右到左/从左到右布局的响应式考量
i18n-date-patterns
Capability Details
能力详情
container-queries
container-queries
Keywords: @container, container-type, inline-size, container-name
Solves: Component-level responsive design
关键词: @container, container-type, inline-size, container-name
解决问题: 组件级别的响应式设计
fluid-typography
fluid-typography
Keywords: clamp(), fluid, vw, rem, scale, typography
Solves: Smooth font scaling without breakpoints
关键词: clamp(), fluid, vw, rem, scale, typography
解决问题: 无需断点即可实现平滑的字体缩放
responsive-images
responsive-images
Keywords: srcset, sizes, picture, art direction
Solves: Responsive images for different viewports
关键词: srcset, sizes, picture, art direction
解决问题: 为不同视口提供响应式图片
mobile-first-strategy
mobile-first-strategy
Keywords: min-width, mobile, progressive, breakpoints
Solves: Efficient responsive CSS architecture
关键词: min-width, mobile, progressive, breakpoints
解决问题: 高效的响应式CSS架构
grid-flexbox-patterns
grid-flexbox-patterns
Keywords: auto-fit, auto-fill, subgrid, minmax
Solves: Responsive grid and flexbox layouts
关键词: auto-fit, auto-fill, subgrid, minmax
解决问题: 响应式网格和弹性盒布局
container-units
container-units
Keywords: cqi, cqb, container width, container height
Solves: Sizing relative to container dimensions
关键词: cqi, cqb, container width, container height
解决问题: 基于容器尺寸的元素 sizing
References
参考资料
- - Container query patterns
references/container-queries.md - - Accessible fluid type scales
references/fluid-typography.md - - Responsive card component
scripts/responsive-card.tsx
- - 容器查询设计模式
references/container-queries.md - - 符合无障碍标准的流体排版规范
references/fluid-typography.md - - 响应式卡片组件示例
scripts/responsive-card.tsx