CSS Container Queries

章节简介

CSS Container Queries是CSS中一项革命性的新特性,它允许开发者根据元素的父容器大小而不是视口大小来应用样式。这一特性解决了传统响应式设计中的许多限制,使得组件级的响应式设计成为可能。本章节将详细介绍CSS Container Queries的基本概念、语法、使用方法和实际应用案例,帮助您掌握这一强大的响应式设计工具。

核心知识点

1. 容器查询的基本概念

容器查询是一种CSS特性,允许开发者根据元素的父容器的大小来应用不同的样式,而不是像传统媒体查询那样依赖于视口大小。

容器查询解决的问题

  • 传统响应式设计依赖于视口大小,难以实现组件级的响应式行为
  • 组件在不同容器中需要不同的样式,但传统方法难以处理
  • 容器查询使得组件可以根据其直接容器的大小自动调整样式
  • 提高了组件的可重用性和灵活性

2. 容器查询的语法

要使用容器查询,首先需要定义一个容器上下文,然后在该上下文中使用容器查询。

定义容器上下文

/* 定义一个容器上下文 */
.container {
  container-type: inline-size; /* 基于内联尺寸(宽度)创建容器上下文 */
}

/* 定义一个基于块尺寸(高度)的容器上下文 */
.container {
  container-type: block-size;
}

/* 定义一个基于 both 尺寸的容器上下文 */
.container {
  container-type: size;
}

/* 为容器指定名称 */
.container {
  container-type: inline-size;
  container-name: my-container;
}

使用容器查询

/* 基本容器查询 */
@container (min-width: 300px) {
  .card {
    /* 当容器宽度至少为300px时应用的样式 */
  }
}

/* 使用命名容器 */
@container my-container (min-width: 300px) {
  .card {
    /* 当名为my-container的容器宽度至少为300px时应用的样式 */
  }
}

/* 组合条件 */
@container (min-width: 300px) and (max-width: 600px) {
  .card {
    /* 当容器宽度在300px到600px之间时应用的样式 */
  }
}

3. 容器查询的使用方法

要使用容器查询,需要遵循以下步骤:

  1. 选择一个元素作为容器,并为其定义容器类型
  2. 在容器内部放置需要响应式调整的元素
  3. 使用@container规则根据容器的大小应用不同的样式

基本示例

/* 定义容器 */
.card-container {
  container-type: inline-size;
  width: 50%;
  border: 1px solid #ccc;
  padding: 20px;
}

/* 卡片样式 */
.card {
  background-color: #f8f9fa;
  border-radius: 8px;
  padding: 15px;
}

/* 容器查询 */
@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 20px;
  }
  
  .card img {
    width: 150px;
    height: 150px;
    object-fit: cover;
  }
}

4. 容器查询的高级特性

容器查询长度单位
容器查询引入了新的长度单位,如cqw(容器查询宽度的1%)、cqh(容器查询高度的1%)、cqi(容器查询内联尺寸的1%)、cqb(容器查询块尺寸的1%)等。

容器查询与网格和弹性布局
容器查询可以与CSS Grid和Flexbox结合使用,创建更加灵活的响应式布局。

嵌套容器查询
可以在一个容器内部创建另一个容器,形成嵌套的容器查询结构。

容器查询与动画
容器查询可以与CSS动画结合使用,创建基于容器大小变化的动画效果。

5. 浏览器兼容性

容器查询是CSS的较新特性,浏览器支持情况如下:

  • Chrome:自版本105起支持
  • Firefox:自版本110起支持
  • Safari:自版本16起支持
  • Edge:自版本105起支持(基于Chromium)

注意:在使用容器查询时,建议提供降级方案,以确保在不支持容器查询的浏览器中也能正常显示。

实用案例

案例1:响应式卡片布局

HTML结构

<div class="card-grid">
  <div class="card-container">
    <div class="card">
      <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=product%20card%20image&image_size=square" alt="产品图片">
      <div class="card-content">
        <h3>产品标题</h3>
        <p>这是产品的简短描述,介绍产品的主要特点和优势。</p>
        <button class="btn">查看详情</button>
      </div>
    </div>
  </div>
  
  <div class="card-container">
    <div class="card">
      <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=product%20card%20image%202&image_size=square" alt="产品图片">
      <div class="card-content">
        <h3>产品标题</h3>
        <p>这是产品的简短描述,介绍产品的主要特点和优势。</p>
        <button class="btn">查看详情</button>
      </div>
    </div>
  </div>
  
  <div class="card-container">
    <div class="card">
      <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=product%20card%20image%203&image_size=square" alt="产品图片">
      <div class="card-content">
        <h3>产品标题</h3>
        <p>这是产品的简短描述,介绍产品的主要特点和优势。</p>
        <button class="btn">查看详情</button>
      </div>
    </div>
  </div>
</div>

CSS样式

/* 卡片网格 */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

/* 卡片容器 */
.card-container {
  container-type: inline-size;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* 卡片样式 */
.card {
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
}

.card img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card-content {
  padding: 15px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.card-content h3 {
  margin: 0 0 10px;
  font-size: 1.1rem;
  color: #333;
}

.card-content p {
  margin: 0 0 15px;
  line-height: 1.5;
  color: #666;
  font-size: 0.9rem;
  flex: 1;
}

.btn {
  padding: 8px 16px;
  background-color: #0066cc;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.9rem;
  align-self: flex-start;
  transition: background-color 0.2s ease;
}

.btn:hover {
  background-color: #004080;
}

/* 容器查询 */
@container (min-width: 350px) {
  .card {
    flex-direction: row;
  }
  
  .card img {
    width: 120px;
    height: 100%;
  }
  
  .card-content {
    padding: 15px;
  }
  
  .card-content h3 {
    font-size: 1.2rem;
  }
}

@container (min-width: 450px) {
  .card img {
    width: 150px;
  }
  
  .card-content h3 {
    font-size: 1.3rem;
  }
  
  .card-content p {
    font-size: 1rem;
  }
}

案例2:响应式导航菜单

HTML结构

<nav class="nav-container">
  <div class="nav-content">
    <div class="logo">
      <h1>我的网站</h1>
    </div>
    <ul class="nav-menu">
      <li><a href="#">首页</a></li>
      <li><a href="#">关于我们</a></li>
      <li><a href="#">服务</a></li>
      <li><a href="#">联系我们</a></li>
    </ul>
    <div class="nav-actions">
      <button class="btn">登录</button>
      <button class="btn btn-primary">注册</button>
    </div>
  </div>
</nav>

CSS样式

/* 导航容器 */
.nav-container {
  container-type: inline-size;
  background-color: #ffffff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  padding: 0 20px;
}

/* 导航内容 */
.nav-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1200px;
  margin: 0 auto;
  height: 60px;
}

/*  logo */
.logo h1 {
  margin: 0;
  font-size: 1.2rem;
  color: #333;
}

/* 导航菜单 */
.nav-menu {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  gap: 20px;
}

.nav-menu a {
  text-decoration: none;
  color: #666;
  font-size: 0.9rem;
  transition: color 0.2s ease;
}

.nav-menu a:hover {
  color: #0066cc;
}

/* 导航操作 */
.nav-actions {
  display: flex;
  gap: 10px;
}

.btn {
  padding: 6px 12px;
  background-color: transparent;
  color: #666;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.8rem;
  transition: all 0.2s ease;
}

.btn:hover {
  background-color: #f5f5f5;
}

.btn-primary {
  background-color: #0066cc;
  color: white;
  border-color: #0066cc;
}

.btn-primary:hover {
  background-color: #004080;
  border-color: #004080;
}

/* 容器查询 */
@container (max-width: 768px) {
  .nav-menu {
    display: none;
  }
  
  .nav-actions {
    gap: 5px;
  }
  
  .btn {
    padding: 4px 8px;
    font-size: 0.7rem;
  }
}

@container (max-width: 480px) {
  .nav-actions {
    display: none;
  }
  
  .logo h1 {
    font-size: 1rem;
  }
}

案例3:响应式仪表盘小部件

HTML结构

<div class="dashboard">
  <div class="widget-container">
    <div class="widget">
      <div class="widget-header">
        <h3>用户统计</h3>
        <span class="widget-badge">今日</span>
      </div>
      <div class="widget-content">
        <div class="stats">
          <div class="stat-item">
            <div class="stat-value">1,234</div>
            <div class="stat-label">总用户</div>
          </div>
          <div class="stat-item">
            <div class="stat-value">56</div>
            <div class="stat-label">新增用户</div>
          </div>
          <div class="stat-item">
            <div class="stat-value">78%</div>
            <div class="stat-label">活跃用户</div>
          </div>
        </div>
        <div class="chart-placeholder">
          <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=dashboard%20chart%20visualization&image_size=landscape_4_3" alt="用户统计图表">
        </div>
      </div>
    </div>
  </div>
  
  <div class="widget-container">
    <div class="widget">
      <div class="widget-header">
        <h3>销售统计</h3>
        <span class="widget-badge">本月</span>
      </div>
      <div class="widget-content">
        <div class="stats">
          <div class="stat-item">
            <div class="stat-value">$45,678</div>
            <div class="stat-label">总销售额</div>
          </div>
          <div class="stat-item">
            <div class="stat-value">12.5%</div>
            <div class="stat-label">增长率</div>
          </div>
          <div class="stat-item">
            <div class="stat-value">342</div>
            <div class="stat-label">订单数</div>
          </div>
        </div>
        <div class="chart-placeholder">
          <img src="https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=sales%20chart%20visualization&image_size=landscape_4_3" alt="销售统计图表">
        </div>
      </div>
    </div>
  </div>
</div>

CSS样式

/* 仪表盘 */
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

/* 小部件容器 */
.widget-container {
  container-type: inline-size;
}

/* 小部件 */
.widget {
  background-color: #ffffff;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

/* 小部件头部 */
.widget-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px;
  border-bottom: 1px solid #e0e0e0;
  background-color: #f8f9fa;
}

.widget-header h3 {
  margin: 0;
  font-size: 1rem;
  color: #333;
}

.widget-badge {
  padding: 2px 8px;
  background-color: #0066cc;
  color: white;
  border-radius: 12px;
  font-size: 0.7rem;
  font-weight: 600;
}

/* 小部件内容 */
.widget-content {
  padding: 15px;
}

/* 统计数据 */
.stats {
  display: flex;
  gap: 15px;
  margin-bottom: 15px;
}

.stat-item {
  flex: 1;
  text-align: center;
}

.stat-value {
  font-size: 1.2rem;
  font-weight: bold;
  color: #0066cc;
  margin-bottom: 4px;
}

.stat-label {
  font-size: 0.7rem;
  color: #666;
}

/* 图表占位符 */
.chart-placeholder {
  width: 100%;
  height: 150px;
  overflow: hidden;
  border-radius: 4px;
}

.chart-placeholder img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 容器查询 */
@container (min-width: 400px) {
  .stats {
    gap: 20px;
  }
  
  .stat-value {
    font-size: 1.4rem;
  }
  
  .stat-label {
    font-size: 0.8rem;
  }
  
  .chart-placeholder {
    height: 180px;
  }
}

@container (min-width: 500px) {
  .widget-content {
    padding: 20px;
  }
  
  .widget-header {
    padding: 20px;
  }
  
  .widget-header h3 {
    font-size: 1.1rem;
  }
  
  .chart-placeholder {
    height: 200px;
  }
}

@container (min-width: 600px) {
  .stats {
    gap: 30px;
  }
  
  .stat-value {
    font-size: 1.6rem;
  }
  
  .stat-label {
    font-size: 0.9rem;
  }
  
  .chart-placeholder {
    height: 220px;
  }
}

章节总结

本章节介绍了CSS Container Queries(容器查询)的核心概念和实践方法,包括:

  1. 容器查询的基本概念:容器查询允许开发者根据元素的父容器大小而不是视口大小来应用样式,解决了传统响应式设计的限制。

  2. 容器查询的语法:使用container-type定义容器上下文,使用@container规则创建容器查询。

  3. 容器查询的使用方法:定义容器、放置内容、应用容器查询样式。

  4. 容器查询的高级特性:包括容器查询长度单位、与网格和弹性布局的结合、嵌套容器查询、与动画的结合等。

  5. 浏览器兼容性:了解容器查询的浏览器支持情况,并提供降级方案。

通过本章节的学习,您应该能够:

  • 理解容器查询的基本概念和优势
  • 使用容器查询创建响应式组件
  • 应用容器查询解决实际布局问题
  • 掌握容器查询的高级特性
  • 考虑容器查询的浏览器兼容性

容器查询是CSS中一项革命性的新特性,它使得组件级的响应式设计成为可能,提高了组件的可重用性和灵活性。随着浏览器支持的不断完善,容器查询将成为现代前端开发中的重要工具之一。

在未来的项目中,建议您尝试使用容器查询来替代部分传统媒体查询,特别是在需要组件级响应式行为的场景中。同时,关注CSS的最新发展,了解更多即将推出的特性,以便更好地应用于实际项目中。

« 上一篇 CSS Grid Level 2 - Subgrid 下一篇 » CSS Nesting