Đọc thứ tự cascade trước khi thêm important
Truy vết màu liên kết qua layer, style nội tuyến, specificity và scope bằng sáu thí nghiệm có khôi phục trạng thái trước khi ghi đè CSS.

Nếu đã đọc được selector CSS cơ bản, bạn có thể dùng thí nghiệm này để giải thích vì sao một liên kết bỏ qua quy tắc có ID. Cascade so sánh nhiều loại ưu tiên trước specificity, tức độ đặc hiệu của selector. Thêm class không vượt qua được một tiêu chí đã phân thắng thua ở bước trước; thêm !important còn làm thay đổi thứ tự được áp dụng.
Ta sẽ xem một thuộc tính duy nhất, màu chữ của liên kết, trên trang riêng. Đây là ví dụ giảng giải, không phải mô tả stylesheet thực tế của portfolio này. Trường hợp đầu không có transition, animation, shadow tree hay style nội tuyến. Khi áp dụng kết quả ở nơi khác, bạn cần kiểm tra lại các điều kiện đó.
Cho hai quy tắc cùng khớp nhưng khác màu
Đặt HTML và stylesheet dưới đây vào tài liệu HTML trống. Điều khiển vẫn là liên kết gốc của trình duyệt: Tab đưa focus đến nó, Enter đi theo fragment. Hình thức giống nút không làm đổi hành vi ấy và cũng không đòi hỏi role button.
<section class="demo" id="editor" aria-labelledby="demo-title">
<h2 id="demo-title">Liên kết có hình thức như nút</h2>
<a class="button text-warning" href="#cascade-notes">Đọc phần ghi chú</a>
<p id="cascade-notes">Liên kết dẫn đến đây. Màu chữ là thuộc tính cần thử.</p>
</section>
@layer base, components, utilities;
@layer base {
.demo {
box-sizing: border-box;
max-inline-size: 40rem;
padding: 1rem;
color: #303326;
background: #f4f0e6;
overflow-wrap: anywhere;
}
.button {
display: inline-block;
box-sizing: border-box;
max-inline-size: 100%;
padding: .6rem 1rem;
border: 1px solid currentColor;
font: inherit;
}
.button:focus-visible {
outline: 3px solid currentColor;
outline-offset: 3px;
}
}
@layer components {
#editor .button { color: darkolivegreen; }
}
@layer utilities {
.text-warning { color: maroon; }
}
Liên kết có màu maroon. Cả hai quy tắc màu đều khớp, nhưng khai báo thường trong layer utilities đứng sau có ưu tiên cao hơn khai báo thường trong components. ID chưa có cơ hội quyết định kết quả. Thứ tự layer được xác lập khi chúng xuất hiện lần đầu; mở lại components ở cuối tệp chỉ thêm quy tắc, không chuyển layer này xuống sau utilities.
Với các quy tắc thông thường do trang khai báo ở đây, khai báo thường ngoài layer có ưu tiên cao hơn mọi khai báo thường trong layer. Đây là điểm dễ sai khi chuyển đổi stylesheet: đưa quy tắc component mới vào layer nhưng để quy tắc cũ ở ngoài có thể khiến quy tắc cũ mạnh hơn tương đối. Import ở phía sau hoặc viết selector dài hơn không tự sửa được quan hệ đó.
Tài liệu về layer giải thích cả thứ tự theo lần khai báo đầu lẫn cách xử lý riêng với khai báo important. Hãy xác lập các layer có tên trước khi nạp quy tắc dùng chúng. Đưa CSS của thư viện vào layer đã chọn có thể hữu ích, nhưng thao tác ấy thay đổi ưu tiên và cần rà soát hồi quy.
Đọc từng cột từ ưu tiên cao xuống thấp. Hình chỉ so sánh khai báo cùng nguồn do trang cung cấp trong ngữ cảnh tài liệu này. Nó không đặt CSS của trang lên trên tùy chỉnh của người dùng hay transition đang hoạt động.
Chạy sáu thay đổi có kiểm soát
Sau khi liên kết tồn tại, chạy khối dưới đây trong console của trang. Nó tạm chèn một stylesheet và chỉ sửa màu nội tuyến của liên kết. Khối finally khôi phục nguyên thuộc tính style ban đầu, đồng thời xóa stylesheet tạm, kể cả khi việc thu thập một dòng kết quả ném lỗi. Hãy dùng trang riêng: selector không liên quan hoặc hiệu ứng đang chạy sẽ thêm đối thủ vào phép so sánh.
{
const link = document.querySelector('#editor .button');
const originalStyle = link.getAttribute('style');
const probe = document.createElement('style');
const importantRules = `
@layer components {
#editor .button { color: darkolivegreen !important; }
}
@layer utilities {
.text-warning { color: maroon !important; }
}
.text-warning { color: navy !important; }
`;
const rows = [];
document.head.append(probe);
try {
for (const [name, rules, inlinePriority] of [
['normal layers', '', null],
['unlayered normal', '.text-warning { color: navy; }', null],
['inline normal', '', ''],
['component important', `@layer components {
#editor .button { color: darkolivegreen !important; }
}`, null],
['three important rules', importantRules, null],
['inline important', importantRules, 'important'],
]) {
probe.textContent = rules;
link.style.removeProperty('color');
if (inlinePriority !== null) {
link.style.setProperty('color', 'teal', inlinePriority);
}
rows.push({ name, color: getComputedStyle(link).color });
}
console.table(rows);
} finally {
probe.remove();
link.setAttribute('style', originalStyle ?? '');
if (originalStyle === null) link.removeAttribute('style');
}
}
Các dòng kết quả dự kiến là:
| Trường hợp | Màu tính được | Lý do thắng |
|---|---|---|
| normal layers | rgb(128, 0, 0) | utilities là layer sau |
| unlayered normal | rgb(0, 0, 128) | quy tắc thường ngoài layer |
| inline normal | rgb(0, 128, 128) | khai báo thường nội tuyến |
| component important | rgb(85, 107, 47) | important thắng thường ở đây |
| three important rules | rgb(85, 107, 47) | layer important đứng trước |
| inline important | rgb(0, 128, 128) | khai báo important nội tuyến |
Ở dòng thứ năm, quy tắc component thắng cả layer utilities đứng sau lẫn quy tắc important ngoài layer. Thứ tự ưu tiên layer bị đảo với khai báo important. Nếu base có quy tắc important, nó sẽ thắng quy tắc component. Style important nội tuyến vẫn thắng các khai báo important trong stylesheet thuộc cùng ngữ cảnh do trang cung cấp; coi style nội tuyến như thêm một điểm ID sẽ che khuất ranh giới này.
Đây là quan sát màu tính được ở trạng thái tĩnh, không phải đo thời điểm vẽ. Đặc tả thứ tự cascade mô tả nguồn khai báo, mức important, style gắn trực tiếp vào phần tử và layer. Khai báo important từ nguồn người dùng và trình duyệt có thể thắng important từ trang. Stylesheet của website không thể giành ưu tiên cao nhất chỉ bằng cách tích thêm cờ important.
Specificity so sánh những quy tắc còn lại
Chuyển cả hai quy tắc màu ban đầu vào components thì selector có ID thắng. Lúc này chúng có cùng nguồn, mức important và layer liên quan. Nếu thay bằng hai selector class cùng khớp và có cùng specificity trong ngữ cảnh ấy, quy tắc xuất hiện sau thắng, miễn là khoảng cách scope không tạo ra khác biệt trước đó.
Specificity là bộ thành phần so sánh từ trái sang phải, không phải điểm số thập phân. Thành phần ID thắng bất kỳ số lượng thành phần class nào. Selector class, thuộc tính và phần lớn pseudo-class nằm ở thành phần tiếp theo; selector tên thẻ và pseudo-element nằm ở thành phần cuối. Các ví dụ sau dùng cách viết ba thành phần trong đặc tả Selectors.
| Selector | Specificity |
|---|---|
| #editor .button | (1, 1, 0) |
| .text-warning | (0, 1, 0) |
| :where(#editor) .button | (0, 1, 0) |
| :is(#missing, .text-warning) | (1, 0, 0) |
Selector cuối khớp qua class, nhưng đối số có specificity cao nhất lại chứa ID. Đối số không cần là nhánh khớp để góp trọng số đó. Theo các quy tắc này, :is, :not và :has dùng đối số có specificity cao nhất; còn :where cùng các đối số đóng góp bằng không. Vì thế một selector nhìn ngắn vẫn có thể thắng selector dài hơn.
Dùng bảng các quy tắc khớp trong công cụ phát triển của trình duyệt để tìm khai báo thắng và layer của nó. Chỉ nhìn màu tính được không cho biết quy tắc nào cung cấp giá trị, nhất là khi hai đối thủ tình cờ dùng cùng màu. Hãy tạm thay giá trị của một đối thủ hoặc xem nguồn quy tắc, thay vì suy ra thứ tự ưu tiên từ một điểm ảnh không đổi.
Scope gần hơn có thể thắng thứ tự trong tệp
Ví dụ riêng này cần trình duyệt hỗ trợ @scope. Thêm HTML và CSS dưới đây ở ngoài phần ví dụ trước. Cả hai quy tắc đều là khai báo thường do trang cung cấp, nằm ngoài layer và có specificity bằng nhau. Quy tắc inner xuất hiện trước, nhưng gốc scope của nó gần đoạn văn hơn.
<div class="outer">
<div class="inner">
<p class="note">Ghi chú nằm trong hai gốc phạm vi.</p>
</div>
</div>
@scope (.inner) {
.note { color: darkolivegreen; }
}
@scope (.outer) {
.note { color: maroon; }
}
Đoạn văn có màu darkolivegreen: gốc inner cách nó một bậc tổ tiên, còn gốc outer cách hai bậc. Khoảng cách scope phân thắng thua trước thứ tự xuất hiện. Chỉ đổi selector trong quy tắc outer thành .note.note thì maroon thắng. Specificity được so trước khoảng cách, nên gốc gần hơn không tạo lá chắn tuyệt đối trước selector mạnh hơn.
Selector chỉ định gốc scope không tự cộng specificity vào quy tắc bên trong. Nếu chính quy tắc ấy viết rõ :scope, pseudo-class đó sẽ đóng góp specificity. Tài liệu scope giải thích khác biệt này cùng phép so khoảng cách. Trình duyệt không phân tích được các khối scope sẽ bỏ qua khai báo trong đó; màu chữ mặc định không phải kết quả mâu thuẫn với cascade. Hãy xem quy tắc đã được phân tích và yêu cầu hỗ trợ trình duyệt trước khi dùng tính năng này.
Kế thừa và hiệu ứng cần câu hỏi khác
Khai báo trên phần tử cha không tham gia trực tiếp vào cascade của con chỉ vì selector mạnh hơn. Cho section màu purple với important: liên kết vẫn có khai báo maroon riêng. Nếu khai báo màu thắng trên chính liên kết có giá trị inherit, nó mới yêu cầu màu tính được của cha. Trước hết hãy xác định đúng phần tử có thuộc tính cần xem.
Custom property tạo thêm một đường truy vết. Cả giá trị thắng của biến lẫn khai báo tiêu thụ nó đều phải đúng. Khai báo padding thắng cascade vẫn có thể mất hợp lệ khi thay giá trị biến vào. Tăng specificity của bên tiêu thụ không sửa được giá trị không hợp lệ.
Hiệu ứng cũng làm đổi phép so sánh. CSS animation thắng khai báo thường nhưng thua khai báo important; giá trị do transition đang hoạt động tạo ra còn thắng cả important trong lúc chuyển tiếp. Tổng quan cascade đặt các hiệu ứng này vào thứ tự ưu tiên. Hãy xem animation và transition đang chạy, thay vì coi một màu trung gian là bằng chứng quy tắc tĩnh đã thua.
Để luyện tập, hãy dự đoán trước từng sửa đổi: mở lại components sau utilities, đặt hai quy tắc class cùng specificity vào utilities, rồi chuyển quy tắc ghi đè ngoài layer vào base. Ghi lại quy tắc thắng và tiêu chí đầu tiên phân biệt nó với quy tắc thua. Cuối cùng thử liên kết bằng bàn phím và khi tăng cỡ chữ. Giải thích đúng thứ tự ưu tiên cần đi cùng một điều khiển dùng được, không thay thế việc kiểm tra ấy.