Category: reference
CSS Container Queries — Responsive ตาม Container ไม่ใช่ Viewport
Container queries ให้ component ปรับ layout ตามขนาดของ parent container แทน viewport — ทำให้ component เป็น truly reusable ในทุกที่ที่วาง
สารบัญ
ปัญหาของ Media Queries
/* Media query ตาม viewport — component ไม่รู้ว่าตัวเองอยู่ที่ไหน */
@media (max-width: 640px) {
.card { flex-direction: column; }
}
/* ปัญหา: ถ้า card อยู่ใน sidebar (narrow) บน desktop
viewport กว้าง 1200px → media query ไม่ทำงาน
แต่ card ในความเป็นจริงแคบมาก */
Container queries แก้ปัญหานี้ — component ตอบสนองต่อขนาดของ parent ของตัวเอง
Setup พื้นฐาน
/* 1. กำหนด container context บน parent */
.card-grid {
container-type: inline-size;
container-name: card-wrapper; /* optional แต่แนะนำ */
}
/* 2. ใช้ @container แทน @media */
@container card-wrapper (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
gap: 1rem;
}
}
@container card-wrapper (min-width: 600px) {
.card {
padding: 2rem;
}
}
container-type Options
/* inline-size — ตรวจ width ของ container (ใช้บ่อยที่สุด) */
.parent { container-type: inline-size; }
/* size — ตรวจทั้ง width และ height */
.parent { container-type: size; }
/* normal — container แต่ไม่มี size containment (สำหรับ style queries) */
.parent { container-type: normal; }
ตัวอย่าง Card Component ที่ Reusable จริงๆ
.article-wrapper {
container-type: inline-size;
container-name: article;
}
.article-card {
/* mobile-first default: vertical layout */
display: grid;
gap: 0.75rem;
}
.article-card img {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 8px;
}
/* เมื่อ container กว้างพอ: horizontal layout */
@container article (min-width: 400px) {
.article-card {
grid-template-columns: 160px 1fr;
align-items: start;
}
.article-card img {
aspect-ratio: 1;
}
}
@container article (min-width: 600px) {
.article-card {
grid-template-columns: 240px 1fr;
gap: 1.5rem;
}
}
Container Style Queries
ตรวจ CSS custom properties บน container:
.card-wrapper {
container-type: normal;
--variant: compact; /* เปลี่ยนได้ผ่าน JavaScript หรือ parent class */
}
@container style(--variant: compact) {
.card { padding: 0.5rem; font-size: 0.85rem; }
}
@container style(--variant: featured) {
.card { padding: 2rem; font-size: 1.1rem; font-weight: bold; }
}
เปรียบเทียบ: Container vs Media Query
| Use Case | ใช้ | เหตุผล |
|---|---|---|
| Card ใน grid | Container | ปรับตาม column width ที่เปลี่ยนตาม screen |
| Sidebar nav | Container | nav อาจแคบหรือกว้างขึ้นกับ layout |
| Full-width banner | Media query | สัมพันธ์กับ viewport โดยตรง |
| Print styles | Media query | ไม่มี container บน print |
| Font size body text | Media query | scale ตาม screen ใหญ่ขึ้น |
ใช้ร่วมกันได้ — container queries ไม่ได้แทน media queries ทั้งหมด
Browser Support
Chrome 105+, Firefox 110+, Safari 16+ — รองรับดีแล้ว
ตรวจก่อนใช้ด้วย @supports:
@supports (container-type: inline-size) {
.wrapper { container-type: inline-size; }
@container (min-width: 400px) {
.card { flex-direction: row; }
}
}
ใช้กับ Astro Component
<!-- Card.astro -->
<div class="card-container">
<article class="card">
<img src={image} alt={title} />
<div class="card-body">
<h3>{title}</h3>
<p>{description}</p>
</div>
</article>
</div>
<style>
.card-container {
container-type: inline-size;
}
.card {
display: grid;
gap: 0.75rem;
}
@container (min-width: 360px) {
.card {
grid-template-columns: 120px 1fr;
}
}
</style>
Component นี้ทำงานถูกต้องไม่ว่าจะวางไว้ใน main content หรือ sidebar — ปรับตาม parent เสมอ