Cũng giống như JavaScript, CSS không có tiếng tăm tốt lắm trong mắt các lập trình viên (đặc biệt là Rails). Và cũng giống như JavaScript (nghĩ đến Turbo hoặc CoffeeScript), CSS có một lịch sử dài về các trình tiền xử lý, hậu xử lý và các dạng trừu tượng hóa (như Tailwind CSS).
Trong một thời gian dài, những công cụ này thực sự cần thiết. Thật khó để tưởng tượng CSS mà không có khả năng lồng selector, đúng không?
Nhưng bây giờ đã là năm 2025 và CSS đang phát triển với tốc độ chóng mặt. Thời của spacer.gif hay tạo ảnh riêng cho từng góc của một thẻ để giả lập bo góc đã qua rồi.
Nếu bạn đã "bỏ bê" CSS một thời gian (ví dụ vì bạn dùng Tailwind CSS), thì dưới đây là một số tính năng CSS mới mà tôi đang rất thích sử dụng trong vài dự án (tự làm hoặc cho khách hàng). Dù vẫn dùng Tailwind CSS, tôi cho rằng hiểu và thật sự sử dụng CSS gốc rất quan trọng. Web được xây dựng trên các chuẩn mở – điều này giúp nó dễ tiếp cận, dễ bảo trì và có thể phát triển về lâu dài.
min(), max() và clamp()
Chúng có liên quan nên được gộp lại.
1. min()
/* Keeps buttons from getting too wide on large screens */
.pricing-button { width: min(300px, 90%);
}
min()
giúp giới hạn tối đa. Nó chọn giá trị nhỏ hơn. Rất tiện để giữ phần tử không quá lớn trong khi vẫn đáp ứng tốt trên nhiều kích thước màn hình.
2. max()
/* Ensures text stays readable even on tiny screens */
.terms-container { font-size: max(16px, 1.2vw);
}
Ngược lại với min()
, max()
đặt ra giới hạn tối thiểu bằng cách chọn giá trị lớn hơn. Rất hữu ích để ngăn phần tử bị thu nhỏ quá mức.
3. clamp()
/* Creates perfectly fluid typography that's never too big or small */
.dashboard-title { font-size: clamp(1.5rem, 5vw, 3rem);
}
clamp()
kết hợp cả min()
và max()
! Nó nhận 3 giá trị:
- Giá trị tối thiểu
- Giá trị ưu tiên
- Giá trị tối đa
Container Queries
Giúp phần tử phản ứng với kích thước của thành phần cha thay vì kích thước toàn bộ trình duyệt như trước.
/* Step 1: Mark the parent as a containment context */
.cards { container-type: inline-size;
} /* Step 2: Base styles for the card component */
.card { padding: .75 1.25rem; background: white; border-radius: 1rem; /* Default to stacked layout */ display: flex; flex-direction: column; gap: .5rem;
} .card__value { font-size: 2rem;
} /* Step 3: Layout changes based on container width */
@container (width > 200px) { .card { /* Switch to horizontal layout when there's enough space */ flex-direction: row; align-items: center; justify-content: space-between; }
}
text-wrap
: Cân bằng & đẹp mắt
Điều khiển cách văn bản xuống dòng nhằm tăng khả năng đọc.
/* text-wrap: balance - ideal for headings */
.section-title { text-wrap: balance; /* Makes lines more visually equal: "Welcome to our Platform" becomes: "Welcome to our Platform" Instead of: "Welcome to our Platform" */
} /* text-wrap: pretty - ideal for paragraphs */
.card-description { text-wrap: pretty; /* Prevents orphaned words: Adds extra line break to avoid: "This is a long description about features" Creates instead: "This is a long description about features" */
}
@starting-style
Cho phép tạo animation từ trạng thái display: none
.
.modal { /* Hidden state */ display: none; opacity: 1;
} .modal.open { display: block; /* Regular transition works now */ transition: opacity 300ms; opacity: 1;
} /* Define starting styles when modal becomes displayed */
@starting-style { .modal.open { opacity: 0; }
}
:has()
Cho phép chọn phần tử cha dựa vào phần tử con – điều mà CSS trước đây không thể làm.
/* Card changes style if it contains an error message */
.card:has(.error-message) { border-color: red; background: rgb(255 0 0 / .05);
} /* Form groups that have required inputs */
.form-group:has(input[required]) { /* Show required indicator */ &::before { content: "*"; color: red; }
}
Media Query Ranges
Cú pháp đơn giản hơn cho min-width
và max-width
.
/* Old syntax */
@media (min-width: 768px) and (max-width: 1199px) { .card { width: 65ch; }
} /* New range syntax */
@media (768px <= width <= 1199px) { .card { width: 65ch; }
} /* Common breakpoint patterns */
.container { /* Smaller than 768px */ @media (width < 768px) { padding: 1rem; } /* Larger than 1200px */ @media (width >= 1200px) { max-width: 1140px; } /* Between breakpoints */ @media (480px <= width < 768px) { margin: 1.5rem; }
}
light-dark
Viết gọn để thay đổi giao diện theo theme sáng hoặc tối.
.card { /* Basic color values */ background: light-dark(#f1f5f9, #0f172a); color: light-dark(#0f172a, #f1f5f9); /* Works with any color format */ border: 1px solid light-dark(rgb(226, 232, 240), rgb(51, 65, 85));
}
color-scheme
Thông báo cho trình duyệt biết thành phần hỗ trợ theme nào.
/* Global level - typically in :root */
:root { /* Support both schemes, dark listed first means dark is preferred */ color-scheme: dark light;
} /* Component level - useful for cards or modals that differ from global scheme */
.themed-card { /* This card only supports light mode */ color-scheme: light; /* System UI elements inside will stay light */ input { /* Inputs remain light-styled even in dark mode */ border: 1px solid #ddd; }
} /* Dark-only interface section */
.code-editor { /* Force dark appearance for all system UI within */ color-scheme: dark;
}
Nesting (Lồng selector)
Tính năng trước đây chỉ có trong Sass/Less – nay đã được hỗ trợ gốc.
.card { padding: .75 1.25rem; background-color: #fff; border-radius: 1rem; /* Nest direct children with & */ & .header { font-size: 1.7rem; } /* Nest state changes */ &:hover { background: #f1f5f9; } /* Nest multiple selectors */ & .title, & .subtitle { font-weight: 600; } /* Nest media queries */ @media (width > 768px) { padding: 1 1.375rem; }
}
Đơn vị đo trong CSS
Font-based:
em
: so với cỡ chữ của phần tử charem
: so với cỡ chữ của:root
ex
: chiều cao ký tự "x"ch
: độ rộng ký tự "0"lh
: bằng với line-height hiện tại
Viewport-based:
vh
,vw
: 1% chiều cao/rộng màn hìnhvmin
,vmax
: dựa trên chiều nhỏ/lớn hơn
Viewport hiện đại:
dvh
,dvw
: điều chỉnh theo thanh điều hướngsvh
,svw
: chiều nhỏ nhất có thểlvh
,lvw
: chiều lớn nhất có thể
@layer
Nếu bạn đã dùng Tailwind CSS, thì @layer
sẽ quen thuộc. Giúp kiểm soát thứ tự ưu tiên giữa các nhóm CSS.
/* Define layer order - order here determines priority */
@layer reset, components, utilities; /* Reset layer: lowest priority */
@layer reset { * { margin: 0; padding: 0; box-sizing: border-box; }
} /* Components layer: middle priority */
@layer components { .button { /* Even if utilities have lower specificity, they'll still override these styles */ padding: .5rem 1rem; background: blue; }
} /* Utilities layer: highest priority */
@layer utilities { .p-4 { /* This wins over component padding */ padding: 1.25rem; } .bg-red { /* This wins over component background */ background: red; }
}
Đây chỉ là phần nổi bật trong số những tính năng CSS hiện đại mà tôi đang sử dụng trong các sản phẩm gần đây. Bạn có học được điều gì mới không? Hãy cho tôi biết nhé!
Cảm ơn các bạn đã theo dõi!