Bài 4. Cascade, kế thừa và độ ưu tiên

Khi nhiều quy tắc cùng nhắm vào một phần tử, quy tắc nào thắng? Bài này giải thích thuật toán cascade: nguồn gốc, độ ưu tiên (specificity), thứ tự, kế thừa, !important và @layer. Hiểu bài này sẽ hết cảnh “sửa CSS mà không ăn”.

45 phútTrung cấp
Bạn sẽ học được
  • Tính được độ ưu tiên của bộ chọn
  • Biết thứ tự quyết định khi hòa
  • Hiểu thuộc tính nào được kế thừa và các từ khóa inherit, initial, unset, revert
  • Dùng @layer thay cho !important
Thuật ngữ cần biết
Specificity
Trọng số của bộ chọn, viết dạng (id, class, thẻ).
Kế thừa
Phần tử con nhận giá trị từ cha nếu thuộc tính cho phép.
@layer
Tầng cascade do bạn đặt thứ tự.

Một số khái niệm

Trình duyệt quyết định theo thứ tự: (1) nguồn gốc và mức quan trọng (CSS mặc định của trình duyệt < CSS của bạn < !important), (2) tầng @layer, (3) độ ưu tiên của bộ chọn, (4) thứ tự xuất hiện: quy tắc viết sau thắng.

Thành phầnTrọng sốVí dụ
Style nội tuyến1-0-0-0style="..."
Id0-1-0-0#menu
Class, thuộc tính, pseudo-class0-0-1-0.nut, [type], :hover
Thẻ, pseudo-element0-0-0-1p, ::before
*, :where()0không cộng

Ví dụ: #menu .muc a:hover = 1 id, 2 (class + pseudo-class), 1 thẻ → (1,2,1). So sánh từ trái sang phải: 1 id luôn thắng 100 class.

Thử nghiệm độ ưu tiên

<style>
  p { color: gray; }                 /* (0,0,1) */
  .ghi-chu { color: blue; }          /* (0,1,0) thắng p */
  #chinh .ghi-chu { color: green; }  /* (1,1,0) thắng .ghi-chu */
  p.ghi-chu { color: orange; }       /* (0,1,1) thua #chinh .ghi-chu */
  .lop { color: purple; }
  .lop { color: crimson; }           /* cùng độ ưu tiên: viết sau thắng */
</style>
<div id="chinh"><p class="ghi-chu">Màu gì? (xanh lá)</p></div>
<p class="ghi-chu">Màu gì? (cam)</p>
<p class="lop">Màu gì? (đỏ thẫm)</p>
Kết quả hiển thị

Kế thừa và từ khóa toàn cục

<style>
  .cha { color: #1565c0; font-family: Georgia, serif; border: 2px dashed #1565c0; padding: 8px; }
  .con-1 { border: inherit; }      /* ép kế thừa border */
  .con-2 { color: initial; }       /* về giá trị mặc định ban đầu (đen) */
  .con-3 { all: unset; }           /* bỏ mọi giá trị đặt cho phần tử */
</style>
<div class="cha">Chữ màu xanh, font Georgia (kế thừa sang con)
  <p>Đoạn con: kế thừa màu và font, KHÔNG kế thừa viền</p>
  <p class="con-1">con-1: border: inherit</p>
  <p class="con-2">con-2: color: initial</p>
</div>
Kết quả hiển thị

Thuộc tính về chữ (color, font-*, line-height, text-align, letter-spacing, visibility, cursor) thường được kế thừa; thuộc tính về hộp (margin, padding, border, background, width) thì không.

@layer và !important

/* Khai báo thứ tự tầng: tầng sau ưu tiên hơn, bất kể độ ưu tiên bộ chọn */
@layer reset, thu-vien, thanh-phan, tien-ich;

@layer thu-vien {
  #app .btn { background: gray; }      /* độ ưu tiên cao nhưng ở tầng thấp */
}
@layer thanh-phan {
  .btn { background: #2e7d32; }        /* thắng, vì tầng thanh-phan sau thu-vien */
}
/* CSS không nằm trong tầng nào ưu tiên hơn mọi tầng */

.an-di { display: none !important; }   /* chỉ dùng cho lớp tiện ích thật sự */

!important đảo ngược thứ tự và rất khó gỡ khi dự án lớn; Bootstrap và Tailwind v4 đều dùng @layer để CSS của bạn ghi đè thư viện dễ dàng.

Mẹo hay

Trong DevTools, rê chuột lên bộ chọn ở ngăn Styles để xem độ ưu tiên (Specificity) dạng (a, b, c).

Lưu ý

Đừng “chữa cháy” bằng cách thêm id hoặc !important mỗi khi CSS không ăn; hãy tìm quy tắc đang thắng trong DevTools rồi viết bộ chọn có độ ưu tiên vừa đủ.

Thực hành
  1. Tính độ ưu tiên của: ul li a, .menu a:hover, #dau .logo img.
  2. Tạo 3 quy tắc xung đột và dự đoán màu cuối cùng trước khi xem.
  3. Viết lại một đoạn CSS dùng !important bằng @layer.
  4. Kiểm tra thuộc tính nào kế thừa bằng cách đặt chúng cho body.
Tóm tắt bài học
  • Thứ tự: nguồn gốc/!important → @layer → độ ưu tiên → thứ tự viết.
  • Độ ưu tiên (id, class, thẻ); nội tuyến cao nhất.
  • Thuộc tính chữ kế thừa; thuộc tính hộp không.
  • inherit, initial, unset, revert, @layer thay !important.