responsive-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Responsive 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)

  1. Viewport 메타태그: HTML에 반드시 포함
    html
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  2. Mobile-First: 모바일 기본, min-width 미디어 쿼리 사용
    • @media (min-width: 768px)
    • @media (max-width: 767px)
      (Desktop-first)
  3. 상대 단위: px 대신 rem, em, %, vw/vh 사용
    • font-size: rem
    • padding/margin: rem 또는 em
    • width: % 또는 vw
  1. Viewport元标签: 必须在HTML中包含
    html
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  2. 移动端优先: 以移动端为基础,使用min-width媒体查询
    • @media (min-width: 768px)
    • @media (max-width: 767px)
      (桌面端优先)
  3. 相对单位: 优先使用rem、em、%、vw/vh替代px
    • 字体大小: rem
    • 内边距/外边距: rem或em
    • 宽度: %或vw

금지 사항 (MUST NOT)

禁止事项(MUST NOT)

  1. 고정 너비 금지:
    width: 1200px
    지양
    • max-width: 1200px
      사용
  2. 중복 코드: 모든 브레이크포인트에 같은 스타일 반복 금지
    • 공통 스타일은 기본으로, 차이만 미디어 쿼리에
  1. 禁止固定宽度: 避免使用
    width: 1200px
    • 推荐使用
      max-width: 1200px
  2. 禁止重复代码: 不要在所有断点中重复相同样式
    • 公共样式写在基础部分,仅在媒体查询中定义差异样式

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

最佳实践

  1. 컨테이너 쿼리 우선: 가능하면 미디어 쿼리 대신 컨테이너 쿼리
  2. Flexbox vs Grid: 1차원은 Flexbox, 2차원은 Grid
  3. 성능: 이미지 lazy loading, WebP 포맷 사용
  4. 테스트: Chrome DevTools Device Mode, BrowserStack
  1. 优先使用容器查询: 尽可能用容器查询替代媒体查询
  2. Flexbox与Grid选择: 一维布局用Flexbox,二维布局用Grid
  3. 性能优化: 图片懒加载、使用WebP格式
  4. 测试验证: 使用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