CSS2022-04-30
返回首页

CSS Container Queries 入门

CSS Container Queries 入门

Container Queries 终于来了,这是 CSS 的重大突破。

为什么需要 Container Queries?

Media Queries 基于视口大小,但有时我们需要基于容器大小:

/* Media Query: 基于视口 */
@media (min-width: 768px) {
  .card {
    display: flex;
  }
}

/* Container Query: 基于容器 */
@container (min-width: 400px) {
  .card {
    display: flex;
  }
}

基本用法

定义容器

/* 定义查询容器 */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* 简写 */
.sidebar {
  container: sidebar / inline-size;
}

使用容器查询

/* 默认样式 */
.card {
  display: block;
}

/* 当容器宽度 >= 400px */
@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
  
  .card-image {
    width: 200px;
  }
}

/* 更宽时 */
@container (min-width: 600px) {
  .card {
    padding: 2rem;
  }
  
  .card-title {
    font-size: 2rem;
  }
}

容器类型

inline-size

基于行内方向尺寸查询:

.container {
  container-type: inline-size;
}

@container (min-width: 500px) {
  /* 基于容器 inline-size */
}

size

基于两个方向尺寸查询:

.container {
  container-type: size;
}

@container (min-width: 500px) and (min-height: 300px) {
  /* 需要两个方向都满足 */
}

命名容器

当嵌套多个容器时,可以指定容器名称:

/* 定义命名容器 */
.card-wrapper {
  container: card / inline-size;
}

.sidebar {
  container: sidebar / inline-size;
}

/* 指定容器查询 */
@container card (min-width: 400px) {
  .card-title {
    font-size: 1.5rem;
  }
}

@container sidebar (min-width: 300px) {
  .widget {
    padding: 1rem;
  }
}

实战案例

响应式卡片组件

<div class="grid">
  <div class="card-container">
    <article class="card">
      <img src="image.jpg" class="card-image" />
      <div class="card-content">
        <h2 class="card-title">标题</h2>
        <p class="card-desc">描述</p>
      </div>
    </article>
  </div>
</div>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}

.card-container {
  container-type: inline-size;
}

/* 默认:垂直布局 */
.card {
  display: flex;
  flex-direction: column;
}

.card-image {
  width: 100%;
  aspect-ratio: 16/9;
  object-fit: cover;
}

/* 容器 >= 400px:水平布局 */
@container (min-width: 400px) {
  .card {
    flex-direction: row;
  }
  
  .card-image {
    width: 200px;
    aspect-ratio: 1;
  }
}

/* 容器 >= 600px:更大的间距 */
@container (min-width: 600px) {
  .card {
    gap: 2rem;
    padding: 2rem;
  }
  
  .card-title {
    font-size: 1.75rem;
  }
}

响应式导航

.nav-container {
  container-type: inline-size;
}

.nav {
  display: flex;
  flex-direction: column;
}

@container (min-width: 500px) {
  .nav {
    flex-direction: row;
    justify-content: space-between;
  }
  
  .nav-menu {
    display: flex;
    gap: 1rem;
  }
}

与 Media Queries 对比

/* Media Query: 全局响应式 */
@media (min-width: 768px) {
  .main-content {
    display: grid;
    grid-template-columns: 300px 1fr;
  }
}

/* Container Query: 组件级响应式 */
@container (min-width: 300px) {
  .sidebar-widget {
    padding: 1rem;
  }
}

查询单位

cqw / cqh

容器查询宽高单位:

.element {
  /* 容器宽度的 10% */
  width: 10cqw;
  
  /* 容器高度的 50% */
  height: 50cqh;
}

/* 响应式字体大小 */
.title {
  font-size: clamp(1rem, 5cqw, 2rem);
}

其他单位

/* 容器 inline 方向 */
font-size: 3cqi;

/* 容器 block 方向 */
padding: 2cqb;

/* min(inline, block) */
gap: 5cqmin;

/* max(inline, block) */
width: 80cqmax;

浏览器支持

/* 渐进增强 */
@supports (container-type: inline-size) {
  .container {
    container-type: inline-size;
  }
  
  @container (min-width: 400px) {
    .card {
      flex-direction: row;
    }
  }
}

/* 降级方案 */
@supports not (container-type: inline-size) {
  @media (min-width: 768px) {
    .card {
      flex-direction: row;
    }
  }
}

最佳实践

  1. 合理选择容器: 确定合适的查询边界
  2. 避免过度嵌套: 减少容器层级
  3. 渐进增强: 提供降级方案
  4. 性能考虑: 避免过多容器查询

小结

Container Queries 要点:

  • 基于容器而非视口查询
  • 定义 container-type
  • 使用 @container 规则

这是组件级响应式设计的重大进步。