Skip to main content
CSS 6 mins read Devs2.org

CSS Container Queries - Cách Viết Component Responsive Độc Lập

Khám phá CSS Container Queries — tính năng cách mạng giúp bạn tạo component responsive độc lập với kích thước viewport. Học cách áp dụng container queries để xây dựng UI linh hoạt hơn bao giờ hết.

#CSS #Container Queries #Web Development #Responsive Design #Frontend #Component Design

Bạn đã quen thuộc với Media Queries — công cụ kinh điển để làm responsive design. Nhưng hãy tưởng tượng bạn có một component card, và khi đặt trong sidebar hẹp thì nó hiển thị dạng dọc, còn khi đặt trong khu vực nội dung rộng thì nó chuyển sang dạng ngang. Với Media Queries, điều này gần như bất khả thi vì component không biết nó đang nằm trong container nào.

Đó chính xác là vấn đề mà CSS Container Queries giải quyết.

css-container-queries-complete-guide

Trong hướng dẫn này, bạn sẽ học mọi thứ về Container Queries — từ khái niệm cơ bản đến các kỹ thuật nâng cao — để xây dựng những component thực sự linh hoạt và độc lập.

CSS Container Queries Là Gì?

Container Queries cho phép bạn áp dụng styles dựa trên kích thước của phần tử cha (container) thay vì kích thước của viewport (màn hình).

Nói cách khác: thay vì hỏi “màn hình rộng bao nhiêu?”, bạn hỏi “khung chứa component này rộng bao nhiêu?”.

Điều này nghe có vẻ nhỏ nhặt, nhưng nó thay đổi hoàn toàn cách chúng ta suy nghĩ về responsive design. Thay vì thiết kế trang web theo từng breakpoint cố định, bạn thiết kế từng component sao cho chúng thích nghi với không gian sẵn có.

Tại Sao Điều Này Quan Trọng?

Hãy xem xét một tình huống thực tế:

  • Bạn có một component card hiển thị 3 cột trên desktop.
  • Component này được nhúng vào một sidebar chỉ rộng 300px.
  • Với Media Queries, card vẫn hiển thị 3 cột vì viewport vẫn rộng — dù sidebar không đủ chỗ!
  • Với Container Queries, card tự động chuyển sang 1 cột vì container của nó chỉ rộng 300px.

Đây chính xác là sức mạnh của Container Queries: component tự biết mình đang ở đâu.

Cú Pháp Cơ Bản

Để sử dụng Container Queries, bạn cần thực hiện 2 bước:

Bước 1: Khai Báo Container

Trước tiên, bạn phải đánh dấu phần tử cha là một container:

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

Thuộc tính container-type nói cho trình duyệt biết phần tử này sẽ là container cho container queries. Có hai giá trị phổ biến:

  • inline-size: Container query dựa trên chiều rộng (trục inline). Đây là giá trị được dùng phổ biến nhất.
  • size: Container query dựa trên cả chiều rộng lẫn chiều cao.

Ngoài ra, bạn có thể đặt tên cho container để dễ quản lý:

.sidebar-widget {
  container-name: sidebar;
  container-type: inline-size;
}

Bước 2: Viết Container Query

Sau đó, bạn viết query giống hệt media query, nhưng dùng @container thay vì @media:

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

.card {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

/* Khi container rộng ít nhất 400px */
@container (min-width: 400px) {
  .card {
    grid-template-columns: 1fr 2fr;
    align-items: center;
  }
}

/* Khi container rộng ít nhất 700px */
@container (min-width: 700px) {
  .card {
    grid-template-columns: 2fr 1fr 1fr;
  }
}

Ví Dụ Thực Tế: Card Component Responsive

Hãy cùng xây dựng một card component hoàn chỉnh sử dụng Container Queries.

HTML Structure

<div class="card-container">
  <article class="card">
    <img src="product.jpg" alt="Product image" class="card-image">
    <div class="card-content">
      <h2 class="card-title">Sản phẩm tuyệt vời</h2>
      <p class="card-description">
        Mô tả ngắn gọn về sản phẩm với thông tin chi tiết.
      </p>
      <button class="card-button">Mua ngay</button>
    </div>
  </article>
</div>

CSS Complete

/* Khai báo container */
.card-container {
  container-type: inline-size;
  container-name: card-area;
}

/* Layout mặc định (container nhỏ) */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border-radius: 12px;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-image {
  width: 100%;
  height: auto;
  border-radius: 8px;
}

.card-title {
  font-size: 1.25rem;
  margin: 0;
}

.card-description {
  color: #666;
  line-height: 1.6;
}

.card-button {
  padding: 0.75rem 1.5rem;
  background: #4f46e5;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-weight: 600;
}

/* Container query: khi container >= 400px */
@container card-area (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card-image {
    width: 200px;
    height: 150px;
    flex-shrink: 0;
  }

  .card-title {
    font-size: 1.5rem;
  }
}

/* Container query: khi container >= 600px */
@container card-area (min-width: 600px) {
  .card {
    padding: 2rem;
    gap: 2rem;
  }

  .card-image {
    width: 280px;
    height: 200px;
  }

  .card-description {
    font-size: 1.1rem;
  }
}

Kết quả: Card sẽ tự động chuyển đổi giữa ba chế độ hiển thị tùy thuộc vào không gian sẵn có trong container của nó.

Named Containers vs Anonymous Containers

Anonymous Container (Mặc Định)

Khi bạn chỉ dùng container-type, container được gọi là anonymous (vô danh). Bạn tham chiếu đến nó bằng cách không đặt tên:

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

@container (min-width: 400px) {
  /* styles */
}

Named Container

Khi bạn đặt tên, bạn có thể tham chiếu rõ ràng:

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

@container sidebar (min-width: 300px) {
  /* styles chỉ áp dụng cho container tên 'sidebar' */
}

Named containers đặc biệt hữu ích khi một trang có nhiều container khác nhau và bạn muốn áp dụng styles khác nhau cho mỗi loại.

Container Style Queries

Một tính năng ít người biết nhưng cực kỳ mạnh mẽ của Container Queries là Style Queries. Khác với size queries (dựa trên kích thước), style queries dựa trên các thuộc tính CSS của container.

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

/* Áp dụng styles khi container có theme='dark-theme' */
@container style(--theme: dark-theme) {
  .card {
    background: #1a1a2e;
    color: white;
  }

  .card-button {
    background: #e94560;
  }
}

Điều này mở ra khả năng tạo component tự thích ứng với theme (sáng/tối) hoặc bất kỳ trạng thái CSS nào khác — hoàn toàn không cần JavaScript!

Kết Hợp Container Queries và Media Queries

Trong thực tế, bạn sẽ thường xuyên kết hợp cả hai:

/* Layout tổng thể của trang dựa trên viewport */
@media (min-width: 768px) {
  .page-layout {
    display: grid;
    grid-template-columns: 250px 1fr;
  }
}

/* Component bên trong tự thích nghi */
.widget-container {
  container-type: inline-size;
}

@container (min-width: 300px) {
  .widget {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}

@container (min-width: 500px) {
  .widget {
    grid-template-columns: repeat(3, 1fr);
  }
}

Ở đây:

  • Media Query quyết định layout tổng thể (sidebar + content).
  • Container Query quyết định cách widget bên trong sidebar hiển thị.

So Sánh: Media Queries vs Container Queries

Tính năngMedia QueriesContainer Queries
Dựa trênKích thước viewportKích thước phần tử cha
Phạm viToàn trangTừng component
Component reuseKhó tái sử dụngDễ dàng tái sử dụng
Nested layoutsPhức tạpTự nhiên
Best forLayout tổng thểComponent design

Khi nào dùng cái nào?

Dùng Media Queries khi:

  • Điều chỉnh layout tổng thể của trang
  • Ẩn/hiện navigation trên mobile
  • Thay đổi số cột của grid layout chính
  • Xử lý các breakpoint thiết bị (mobile, tablet, desktop)

Dùng Container Queries khi:

  • Xây dựng component library reusable
  • Component cần thích nghi theo vị trí embed
  • Dashboard với nhiều widget kích thước khác nhau
  • Sidebar, modal, drawer có kích thước cố định

Case Study: Dashboard Widget System

Hãy xem một ví dụ thực tế hơn — hệ thống dashboard với nhiều widget:

<div class="dashboard">
  <div class="widget-container large">
    <div class="chart-widget">...</div>
  </div>
  <div class="widget-container small">
    <div class="stat-widget">...</div>
  </div>
  <div class="widget-container medium">
    <div class="table-widget">...</div>
  </div>
</div>
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1rem;
}

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

/* Widget nhỏ: hiển thị icon + label */
.stat-widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

@container widget (min-width: 250px) {
  .stat-widget {
    flex-direction: row;
    text-align: left;
    justify-content: space-between;
  }
}

/* Bảng dữ liệu: co giản theo container */
.table-widget table {
  width: 100%;
  font-size: 0.875rem;
}

@container widget (min-width: 400px) {
  .table-widget table {
    font-size: 1rem;
  }
  
  .table-widget td,
  .table-widget th {
    padding: 0.75rem 1rem;
  }
}

Mỗi widget tự điều chỉnh giao diện dựa trên không gian sẵn có — không cần biết viewport rộng hay hẹp.

Performance và Best Practices

✅ Nên làm

  1. Sử dụng container-type: inline-size — phù hợp với hầu hết trường hợp responsive.
  2. Đặt breakpoint hợp lý — dựa trên kích thước container thực tế, không phải viewport.
  3. Kết hợp với CSS Grid/Flexbox — container queries phát huy sức mạnh nhất khi kết hợp với các layout system hiện đại.
  4. Dùng named containers — giúp code dễ đọc và dễ bảo trì.
  5. Test trên nhiều kích thước container — không chỉ viewport, mà cả các container lồng nhau.

❌ Không nên làm

  1. Không thay thế hoàn toàn media queries — mỗi công cụ có mục đích riêng.
  2. Không đặt quá nhiều breakpoints — 2-3 breakpoints là đủ cho hầu hết component.
  3. Không quên fallback — luôn có layout mặc định cho browser chưa hỗ trợ container queries.
  4. Không dùng container queries cho layout trang — vẫn dùng media queries cho cấu trúc tổng thể.

Fallback cho Browser Cũ

Nếu cần hỗ trợ browser cũ chưa hỗ trợ container queries, bạn có thể dùng @supports:

/* Layout mặc định cho tất cả browser */
.card {
  display: flex;
  flex-direction: column;
}

/* Chỉ áp dụng container query nếu browser hỗ trợ */
@supports (container-type: inline-size) {
  @container (min-width: 400px) {
    .card {
      flex-direction: row;
    }
  }
}

Hoặc dùng polyfill containers-polyfill nếu cần hỗ trợ rộng hơn.

Tương Lai Của Container Queries

Container Queries đang mở ra một kỷ nguyên mới cho component-based design. Các tính năng sắp tới bao gồm:

  • Container style queries (đã hỗ trợ trong Chrome 115+)
  • Container size queries (theo cả chiều rộng và chiều cao)
  • Nested containers — container bên trong container
  • Tích hợp tốt hơn với CSS Layers — kiểm soát specificity dễ dàng hơn

Với sự phát triển nhanh chóng, Container Queries sẽ trở thành công cụ không thể thiếu trong toolkit của mọi frontend developer.

Tổng Kết

CSS Container Queries là một bước tiến lớn trong responsive design. Thay vì thiết kế trang web theo breakpoint cố định, chúng ta có thể thiết kế component theo không gian sẵn có — tạo ra UI linh hoạt, dễ tái sử dụng và dễ bảo trì hơn.

Những điểm chính cần nhớ:

  1. Container Queries dựa trên kích thước container, không phải viewport.
  2. Cần khai báo container-type trước khi dùng @container.
  3. Kết hợp với Media Queries để có kết quả tốt nhất.
  4. Perfect cho component library và dashboard systems.
  5. Được hỗ trợ bởi tất cả browser hiện đại.

Hãy bắt đầu thử nghiệm Container Queries trong dự án tiếp theo của bạn — bạn sẽ ngạc nhiên trước sự linh hoạt mà chúng mang lại!


Bạn đã từng gặp vấn đề nào với responsive design mà Media Queries không giải quyết được? Chia sẻ trải nghiệm của bạn trong phần bình luận bên dưới!

Recently Used Tools