responsive-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseResponsive Design
响应式设计
When to use this skill
何时使用该技能
- 새 웹사이트/앱: 모바일-데스크톱 겸용 레이아웃 설계
- 레거시 개선: 고정 레이아웃을 반응형으로 전환
- 성능 최적화: 디바이스별 이미지 최적화
- 다양한 화면: 태블릿, 데스크톱, 대형 화면 지원
- 新网站/应用:设计兼顾移动端与桌面端的布局
- 遗留项目优化:将固定布局转换为响应式布局
- 性能优化:针对不同设备优化图片
- 多屏幕适配:支持平板、桌面端及大屏设备
Instructions
操作步骤
Step 1: Mobile-First 접근
步骤1:移动端优先设计方法
작은 화면부터 설계하고 점진적으로 확장합니다.
예시:
css
/* 기본: 모바일 (320px~) */
.container {
padding: 1rem;
font-size: 14px;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
/* 태블릿 (768px~) */
@media (min-width: 768px) {
.container {
padding: 2rem;
font-size: 16px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
}
/* 데스크톱 (1024px~) */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
/* 대형 화면 (1440px~) */
@media (min-width: 1440px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}从小屏幕开始设计,逐步扩展到更大屏幕。
示例:
css
/* 基础: 移动端 (320px~) */
.container {
padding: 1rem;
font-size: 14px;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
/* 平板 (768px~) */
@media (min-width: 768px) {
.container {
padding: 2rem;
font-size: 16px;
}
.grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
}
/* 桌面端 (1024px~) */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
/* 大屏设备 (1440px~) */
@media (min-width: 1440px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}Step 2: Flexbox/Grid 레이아웃
步骤2:Flexbox/Grid 布局
현대적인 CSS 레이아웃 시스템을 활용합니다.
Flexbox (1차원 레이아웃):
css
/* 네비게이션 바 */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
/* 카드 리스트 */
.card-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.card-list {
flex-direction: row;
flex-wrap: wrap;
}
.card {
flex: 1 1 calc(50% - 0.5rem); /* 2 columns */
}
}
@media (min-width: 1024px) {
.card {
flex: 1 1 calc(33.333% - 0.667rem); /* 3 columns */
}
}CSS Grid (2차원 레이아웃):
css
/* 대시보드 레이아웃 */
.dashboard {
display: grid;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
gap: 1rem;
}
@media (min-width: 768px) {
.dashboard {
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 250px 1fr;
}
}
@media (min-width: 1024px) {
.dashboard {
grid-template-columns: 300px 1fr;
}
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }运用现代CSS布局系统。
Flexbox (一维布局):
css
/* 导航栏 */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
/* 卡片列表 */
.card-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.card-list {
flex-direction: row;
flex-wrap: wrap;
}
.card {
flex: 1 1 calc(50% - 0.5rem); /* 2列布局 */
}
}
@media (min-width: 1024px) {
.card {
flex: 1 1 calc(33.333% - 0.667rem); /* 3列布局 */
}
}CSS Grid (二维布局):
css
/* 仪表盘布局 */
.dashboard {
display: grid;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
gap: 1rem;
}
@media (min-width: 768px) {
.dashboard {
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 250px 1fr;
}
}
@media (min-width: 1024px) {
.dashboard {
grid-template-columns: 300px 1fr;
}
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }Step 3: 반응형 이미지
步骤3:响应式图片
디바이스에 맞는 이미지를 제공합니다.
srcset 사용:
html
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="
(max-width: 600px) 100vw,
(max-width: 900px) 50vw,
33vw
"
alt="Responsive image"
/>picture 요소 (Art Direction):
html
<picture>
<!-- 모바일: 세로 이미지 -->
<source media="(max-width: 767px)" srcset="portrait.jpg">
<!-- 태블릿: 정사각형 이미지 -->
<source media="(max-width: 1023px)" srcset="square.jpg">
<!-- 데스크톱: 가로 이미지 -->
<img src="landscape.jpg" alt="Art direction example">
</picture>CSS 배경 이미지:
css
.hero {
background-image: url('hero-mobile.jpg');
}
@media (min-width: 768px) {
.hero {
background-image: url('hero-tablet.jpg');
}
}
@media (min-width: 1024px) {
.hero {
background-image: url('hero-desktop.jpg');
}
}
/* 또는 image-set() 사용 */
.hero {
background-image: image-set(
url('hero-1x.jpg') 1x,
url('hero-2x.jpg') 2x
);
}为不同设备提供适配的图片。
使用srcset:
html
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="
(max-width: 600px) 100vw,
(max-width: 900px) 50vw,
33vw
"
alt="Responsive image"
/>picture元素 (艺术指导):
html
<picture>
<!-- 移动端: 竖版图片 -->
<source media="(max-width: 767px)" srcset="portrait.jpg">
<!-- 平板: 方形图片 -->
<source media="(max-width: 1023px)" srcset="square.jpg">
<!-- 桌面端: 横版图片 -->
<img src="landscape.jpg" alt="Art direction example">
</picture>CSS背景图片:
css
.hero {
background-image: url('hero-mobile.jpg');
}
@media (min-width: 768px) {
.hero {
background-image: url('hero-tablet.jpg');
}
}
@media (min-width: 1024px) {
.hero {
background-image: url('hero-desktop.jpg');
}
}
/* 或使用image-set() */
.hero {
background-image: image-set(
url('hero-1x.jpg') 1x,
url('hero-2x.jpg') 2x
);
}Step 4: 반응형 타이포그래피
步骤4:响应式排版
화면 크기에 따라 텍스트 크기를 조정합니다.
clamp() 함수 (유동적 크기):
css
:root {
/* min, preferred, max */
--font-size-body: clamp(14px, 2.5vw, 18px);
--font-size-h1: clamp(24px, 5vw, 48px);
--font-size-h2: clamp(20px, 4vw, 36px);
}
body {
font-size: var(--font-size-body);
}
h1 {
font-size: var(--font-size-h1);
line-height: 1.2;
}
h2 {
font-size: var(--font-size-h2);
line-height: 1.3;
}미디어 쿼리 방식:
css
body {
font-size: 14px;
line-height: 1.6;
}
@media (min-width: 768px) {
body { font-size: 16px; }
}
@media (min-width: 1024px) {
body { font-size: 18px; }
}根据屏幕大小调整文本尺寸。
clamp()函数 (动态尺寸):
css
:root {
/* 最小值, 首选值, 最大值 */
--font-size-body: clamp(14px, 2.5vw, 18px);
--font-size-h1: clamp(24px, 5vw, 48px);
--font-size-h2: clamp(20px, 4vw, 36px);
}
body {
font-size: var(--font-size-body);
}
h1 {
font-size: var(--font-size-h1);
line-height: 1.2;
}
h2 {
font-size: var(--font-size-h2);
line-height: 1.3;
}媒体查询方式:
css
body {
font-size: 14px;
line-height: 1.6;
}
@media (min-width: 768px) {
body { font-size: 16px; }
}
@media (min-width: 1024px) {
body { font-size: 18px; }
}Step 5: Container Queries (신기능)
步骤5:容器查询(新功能)
부모 컨테이너 크기에 따라 스타일 적용합니다.
css
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
padding: 1rem;
}
.card h2 {
font-size: 1.2rem;
}
/* 컨테이너가 400px 이상일 때 */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
padding: 1.5rem;
}
.card h2 {
font-size: 1.5rem;
}
}
/* 컨테이너가 600px 이상일 때 */
@container card (min-width: 600px) {
.card {
grid-template-columns: 300px 1fr;
padding: 2rem;
}
}根据父容器的尺寸应用样式。
css
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
padding: 1rem;
}
.card h2 {
font-size: 1.2rem;
}
/* 容器宽度≥400px时 */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
padding: 1.5rem;
}
.card h2 {
font-size: 1.5rem;
}
}
/* 容器宽度≥600px时 */
@container card (min-width: 600px) {
.card {
grid-template-columns: 300px 1fr;
padding: 2rem;
}
}Output format
输出格式
표준 브레이크포인트
标准断点
css
/* Mobile (default): 320px ~ 767px */
/* Tablet: 768px ~ 1023px */
/* Desktop: 1024px ~ 1439px */
/* Large: 1440px+ */
:root {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
/* 사용 예시 */
@media (min-width: 768px) { /* Tablet */ }
@media (min-width: 1024px) { /* Desktop */ }css
/* 移动端(默认): 320px ~ 767px */
/* 平板: 768px ~ 1023px */
/* 桌面端: 1024px ~ 1439px */
/* 大屏设备: 1440px+ */
:root {
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
/* 使用示例 */
@media (min-width: 768px) { /* 平板端 */ }
@media (min-width: 1024px) { /* 桌面端 */ }Constraints
约束规则
필수 규칙 (MUST)
必须遵守的规则(MUST)
-
Viewport 메타태그: HTML에 반드시 포함html
<meta name="viewport" content="width=device-width, initial-scale=1.0"> -
Mobile-First: 모바일 기본, min-width 미디어 쿼리 사용
- ✅
@media (min-width: 768px) - ❌ (Desktop-first)
@media (max-width: 767px)
- ✅
-
상대 단위: px 대신 rem, em, %, vw/vh 사용
- font-size: rem
- padding/margin: rem 또는 em
- width: % 또는 vw
-
Viewport元标签: 必须在HTML中包含html
<meta name="viewport" content="width=device-width, initial-scale=1.0"> -
移动端优先: 以移动端为基础,使用min-width媒体查询
- ✅
@media (min-width: 768px) - ❌ (桌面端优先)
@media (max-width: 767px)
- ✅
-
相对单位: 优先使用rem、em、%、vw/vh替代px
- 字体大小: rem
- 内边距/外边距: rem或em
- 宽度: %或vw
금지 사항 (MUST NOT)
禁止事项(MUST NOT)
-
고정 너비 금지:지양
width: 1200px- 사용
max-width: 1200px
-
중복 코드: 모든 브레이크포인트에 같은 스타일 반복 금지
- 공통 스타일은 기본으로, 차이만 미디어 쿼리에
-
禁止固定宽度: 避免使用
width: 1200px- 推荐使用
max-width: 1200px
- 推荐使用
-
禁止重复代码: 不要在所有断点中重复相同样式
- 公共样式写在基础部分,仅在媒体查询中定义差异样式
Examples
示例
예시 1: 반응형 네비게이션
示例1:响应式导航栏
tsx
function ResponsiveNav() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="navbar">
{/* 로고 */}
<a href="/" className="logo">MyApp</a>
{/* 햄버거 버튼 (모바일) */}
<button
className="menu-toggle"
onClick={() => setIsOpen(!isOpen)}
aria-label="Toggle menu"
aria-expanded={isOpen}
>
<span></span>
<span></span>
<span></span>
</button>
{/* 네비게이션 링크 */}
<ul className={`nav-links ${isOpen ? 'active' : ''}`}>
<li><a href="/about">About</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
);
}css
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
/* 햄버거 버튼 (모바일만) */
.menu-toggle {
display: flex;
flex-direction: column;
gap: 4px;
}
.nav-links {
display: none;
position: absolute;
top: 60px;
left: 0;
right: 0;
background: white;
flex-direction: column;
}
.nav-links.active {
display: flex;
}
/* 태블릿 이상: 햄버거 숨기고 항상 표시 */
@media (min-width: 768px) {
.menu-toggle {
display: none;
}
.nav-links {
display: flex;
position: static;
flex-direction: row;
gap: 2rem;
}
}tsx
function ResponsiveNav() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="navbar">
{/* 品牌Logo */}
<a href="/" className="logo">MyApp</a>
{/* 汉堡菜单按钮(仅移动端显示) */}
<button
className="menu-toggle"
onClick={() => setIsOpen(!isOpen)}
aria-label="Toggle menu"
aria-expanded={isOpen}
>
<span></span>
<span></span>
<span></span>
</button>
{/* 导航链接 */}
<ul className={`nav-links ${isOpen ? 'active' : ''}`}>
<li><a href="/about">About</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
);
}css
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
/* 汉堡菜单按钮(仅移动端显示) */
.menu-toggle {
display: flex;
flex-direction: column;
gap: 4px;
}
.nav-links {
display: none;
position: absolute;
top: 60px;
left: 0;
right: 0;
background: white;
flex-direction: column;
}
.nav-links.active {
display: flex;
}
/* 平板及以上设备:隐藏汉堡菜单,始终显示导航链接 */
@media (min-width: 768px) {
.menu-toggle {
display: none;
}
.nav-links {
display: flex;
position: static;
flex-direction: row;
gap: 2rem;
}
}예시 2: 반응형 그리드 카드
示例2:响应式网格卡片
tsx
function ProductGrid({ products }) {
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">${product.price}</p>
<button>Add to Cart</button>
</div>
))}
</div>
);
}css
.product-grid {
display: grid;
grid-template-columns: 1fr; /* 모바일: 1 column */
gap: 1rem;
padding: 1rem;
}
@media (min-width: 640px) {
.product-grid {
grid-template-columns: repeat(2, 1fr); /* 2 columns */
}
}
@media (min-width: 1024px) {
.product-grid {
grid-template-columns: repeat(3, 1fr); /* 3 columns */
gap: 1.5rem;
}
}
@media (min-width: 1440px) {
.product-grid {
grid-template-columns: repeat(4, 1fr); /* 4 columns */
gap: 2rem;
}
}
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
}
.product-card img {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
object-fit: cover;
}tsx
function ProductGrid({ products }) {
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">${product.price}</p>
<button>Add to Cart</button>
</div>
))}
</div>
);
}css
.product-grid {
display: grid;
grid-template-columns: 1fr; /* 移动端:1列 */
gap: 1rem;
padding: 1rem;
}
@media (min-width: 640px) {
.product-grid {
grid-template-columns: repeat(2, 1fr); /* 2列布局 */
}
}
@media (min-width: 1024px) {
.product-grid {
grid-template-columns: repeat(3, 1fr); /* 3列布局 */
gap: 1.5rem;
}
}
@media (min-width: 1440px) {
.product-grid {
grid-template-columns: repeat(4, 1fr); /* 4列布局 */
gap: 2rem;
}
}
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
}
.product-card img {
width: 100%;
height: auto;
aspect-ratio: 1 / 1;
object-fit: cover;
}Best practices
最佳实践
- 컨테이너 쿼리 우선: 가능하면 미디어 쿼리 대신 컨테이너 쿼리
- Flexbox vs Grid: 1차원은 Flexbox, 2차원은 Grid
- 성능: 이미지 lazy loading, WebP 포맷 사용
- 테스트: Chrome DevTools Device Mode, BrowserStack
- 优先使用容器查询: 尽可能用容器查询替代媒体查询
- Flexbox与Grid选择: 一维布局用Flexbox,二维布局用Grid
- 性能优化: 图片懒加载、使用WebP格式
- 测试验证: 使用Chrome DevTools设备模式、BrowserStack进行测试
References
参考资料
Metadata
元数据
버전
版本
- 현재 버전: 1.0.0
- 최종 업데이트: 2025-01-01
- 호환 플랫폼: Claude, ChatGPT, Gemini
- 当前版本: 1.0.0
- 最后更新: 2025-01-01
- 兼容平台: Claude, ChatGPT, Gemini
관련 스킬
相关技能
- ui-component-patterns: 반응형 컴포넌트
- web-accessibility: 접근성과 함께 고려
- ui-component-patterns: 响应式组件
- web-accessibility: 结合可访问性设计
태그
标签
#responsive#mobile-first#CSS#Flexbox#Grid#media-query#frontend#responsive#mobile-first#CSS#Flexbox#Grid#media-query#frontend